Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ test.py
*.cpython-312.pyc`
file_generator.py
.coverage
.coverage.*
htmlcov/
.env.local
Pipfile
test/
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
# Changelog

## 2.2.90

- Migrated license enrichment PURL lookup to the org-scoped endpoint (`POST /v0/orgs/{slug}/purl`) from the deprecated global endpoint (`POST /v0/purl`).

## 2.2.83

- Fixed branch detection in detached-HEAD CI checkouts. When `git name-rev --name-only HEAD` returned an output with a suffix operator (e.g. `remotes/origin/master~1`, `master^0`), the `~N`/`^N` was previously passed through as the branch name and rejected by the Socket API as an invalid Git ref. The suffix is now stripped before the prefix split, producing the bare branch name.

## 2.2.80

- Hardened GitHub Actions workflows.
- Fixed broken links on PyPI page.

## 2.2.79

- Updated minimum required Python version.
- Tweaked CI checks.

## 2.2.78

- Fixed reachability filtering.
- Added config file support.

## 2.2.77

- Fixed `has_manifest_files` failing to match root-level manifest files.

## 2.2.76

- Added SARIF file output support.
- Improved reachability filtering.

## 2.2.75

- Fixed `workspace` flag regression by updating SDK dependency.

## 2.2.74

- Added `--workspace` flag to CLI args.
- Added GitLab branch protection flag.
- Added e2e tests for full scans and full scans with reachability.
- Bumped dependencies: `cryptography`, `virtualenv`, `filelock`, `urllib3`.

## 2.2.71

- Added `strace` to the Docker image for debugging purposes.
Expand Down
2 changes: 1 addition & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ The CLI will automatically install `@coana-tech/cli` if not present. Use `--reac
| Parameter | Required | Default | Description |
|:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
| `--ignore-commit-files` | False | False | Ignore commit files |
| `--disable-blocking` | False | False | Disable blocking mode |
| `--disable-blocking` | False | False | Non-blocking CI mode: the CLI always exits **0**, even when blocking alerts are present (including with `--strict-blocking`). Also exits 0 on uncaught runtime errors and Socket API failures, so the job is treated as successful while findings and errors are still logged. Takes precedence over `--strict-blocking`. |
| `--disable-ignore` | False | False | Disable support for `@SocketSecurity ignore` commands in PR comments. When set, alerts cannot be suppressed via comments and ignore instructions are hidden from comment output. |
| `--strict-blocking` | False | False | Fail on ANY security policy violations (blocking severity), not just new ones. Only works in diff mode. See [Strict Blocking Mode](#strict-blocking-mode) for details. |
| `--enable-diff` | False | False | Enable diff mode even when using `--integration api` (forces diff mode without SCM integration) |
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"

[project]
name = "socketsecurity"
version = "2.2.89"
version = "2.2.90"
requires-python = ">= 3.11"
license = {"file" = "LICENSE"}
dependencies = [
Expand All @@ -16,7 +16,7 @@ dependencies = [
'GitPython',
'packaging',
'python-dotenv',
"socketdev>=3.0.33,<4.0.0",
"socketdev>=3.1.0,<4.0.0",
"bs4>=0.0.2",
"markdown>=3.10",
]
Expand Down Expand Up @@ -57,7 +57,7 @@ socketcli = "socketsecurity.socketcli:cli"
socketclidev = "socketsecurity.socketcli:cli"

[project.urls]
Homepage = "https://socket.dev"
Homepage = "https://github.com/SocketDev/socket-python-cli"

[tool.coverage.run]
source = ["socketsecurity"]
Expand Down
2 changes: 1 addition & 1 deletion socketsecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__author__ = 'socket.dev'
__version__ = '2.2.89'
__version__ = '2.2.90'
USER_AGENT = f'SocketPythonCLI/{__version__}'
6 changes: 5 additions & 1 deletion socketsecurity/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,11 @@ def create_argument_parser() -> argparse.ArgumentParser:
"--disable-blocking",
dest="disable_blocking",
action="store_true",
help="Disable blocking mode"
help=(
"Non-blocking CI mode: always exit 0, even when blocking alerts are present "
"(including with --strict-blocking), on uncaught errors, or on Socket API failures. "
"Findings and errors are still logged. Overrides --strict-blocking."
),
)
advanced_group.add_argument(
"--disable_blocking",
Expand Down
5 changes: 5 additions & 0 deletions socketsecurity/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,7 @@ def get_license_text_via_purl(self, packages: dict[str, Package], batch_size: in
results = self.sdk.purl.post(
license=True,
components=batch_components,
org_slug=self.config.org_slug,
licenseattrib=True,
licensedetails=True
)
Expand Down Expand Up @@ -946,6 +947,8 @@ def get_added_and_removed_packages(
)
except APIFailure as e:
log.error(f"API Error: {e}")
if self.cli_config and self.cli_config.disable_blocking:
sys.exit(0)
sys.exit(1)
except Exception as e:
import traceback
Expand Down Expand Up @@ -1123,6 +1126,8 @@ def create_new_diff(
os.unlink(temp_file)
except OSError:
pass
if self.cli_config and self.cli_config.disable_blocking:
sys.exit(0)
sys.exit(1)
except Exception as e:
import traceback
Expand Down
39 changes: 38 additions & 1 deletion tests/core/test_package_and_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,41 @@ def test_get_new_alerts_with_readded(self):

# With ignore_readded=False
new_alerts = Core.get_new_alerts(added_alerts, removed_alerts, ignore_readded=False)
assert len(new_alerts) == 1
assert len(new_alerts) == 1

def test_get_license_text_via_purl_uses_org_scoped_endpoint(self, core, mock_sdk):
"""Test license enrichment calls the org-scoped PURL SDK method."""
core.sdk.purl = Mock()
core.sdk.purl.post.return_value = [
{
"type": "npm",
"name": "lodash",
"version": "4.18.1",
"licenseAttrib": [{"name": "MIT"}],
"licenseDetails": [{"license": "MIT"}],
}
]

packages = {
"npm/lodash@4.18.1": Package(
id="pkg:npm/lodash@4.18.1",
type="npm",
name="lodash",
version="4.18.1",
score={},
alerts=[],
topLevelAncestors=[],
)
}

result = core.get_license_text_via_purl(packages)

core.sdk.purl.post.assert_called_once_with(
license=True,
components=[{"purl": "pkg:/npm/lodash@4.18.1"}],
org_slug="test-org",
licenseattrib=True,
licensedetails=True,
)
assert result["npm/lodash@4.18.1"].licenseAttrib == [{"name": "MIT"}]
assert result["npm/lodash@4.18.1"].licenseDetails == [{"license": "MIT"}]
54 changes: 35 additions & 19 deletions tests/e2e/validate-reachability.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,47 @@ else
exit 1
fi

# 3. Run SARIF with --sarif-reachability all
socketcli \
--target-path tests/e2e/fixtures/simple-npm \
--reach \
--sarif-file /tmp/sarif-all.sarif \
--sarif-scope full \
--sarif-reachability all \
--disable-blocking \
2>/dev/null
FACTS_PATH="tests/e2e/fixtures/simple-npm/.socket.facts.json"
if [ ! -f "$FACTS_PATH" ]; then
echo "FAIL: Expected reachability facts at $FACTS_PATH after initial scan"
exit 1
fi
echo "PASS: Reachability facts file present at $FACTS_PATH"

# 3-4. Build SARIF from the facts file produced by the initial --reach run.
# Avoid re-running reach + full scan here; duplicate API scans are slow and flaky in CI.
uv run python -c "
import json
from pathlib import Path

# 4. Run SARIF with --sarif-reachability reachable (filtered)
socketcli \
--target-path tests/e2e/fixtures/simple-npm \
--reach \
--sarif-file /tmp/sarif-reachable.sarif \
--sarif-scope full \
--sarif-reachability reachable \
--disable-blocking \
2>/dev/null
from socketsecurity.core.alert_selection import load_components_with_alerts
from socketsecurity.core.messages import Messages

target = 'tests/e2e/fixtures/simple-npm'
facts_file = '.socket.facts.json'
components = load_components_with_alerts(target, facts_file)
if not components:
raise SystemExit('FAIL: no components with alerts in .socket.facts.json')

for outfile, reach_filter in [
('/tmp/sarif-all.sarif', 'all'),
('/tmp/sarif-reachable.sarif', 'reachable'),
]:
sarif = Messages.create_security_comment_sarif_from_facts(
components,
reachability_filter=reach_filter,
grouping='instance',
)
Path(outfile).write_text(json.dumps(sarif, indent=2))
count = len(sarif['runs'][0]['results'])
print(f'PASS: Wrote {outfile} ({count} results, filter={reach_filter})')
"

# 5. Verify reachable-only results are a subset of all results
test -f /tmp/sarif-all.sarif
test -f /tmp/sarif-reachable.sarif

python3 -c "
uv run python -c "
import json
with open('/tmp/sarif-all.sarif') as f:
all_data = json.load(f)
Expand Down
10 changes: 5 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading