socketsecurity 2.9.7__tar.gz → 2.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/CHANGELOG.md +27 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/PKG-INFO +1 -1
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/docs/cli-reference.md +10 -8
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/pyproject.toml +1 -1
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/config.py +37 -16
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/__init__.py +23 -2
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/git_interface.py +70 -35
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/output.py +18 -7
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/socketcli.py +31 -22
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_diff_generation.py +46 -1
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_cli_config.py +3 -2
- socketsecurity-2.10.0/tests/unit/test_commit_message_truncation.py +113 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_full_scan_retry.py +43 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_git_interface.py +126 -0
- socketsecurity-2.10.0/tests/unit/test_summary_text.py +74 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/uv.lock +1 -1
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/actions/setup-docker/action.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/actions/setup-hatch/action.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/actions/setup-sfw/action.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/dependabot.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/dependency-audit.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/dependency-review.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/e2e-test.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/package-check.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/python-tests.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.github/zizmor.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.gitignore +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/.python-version +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/CONTRIBUTING.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/Dockerfile +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/Dockerfile.preview +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/LICENSE +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/Makefile +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/README.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/benchmarks/manifest_discovery.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/docs/ci-cd.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/docs/development.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/docs/troubleshooting.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-dashboard-parity.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-dashboard-parity.toml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-diff-ci-cd.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-diff-ci-cd.toml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-instance-detail.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/examples/config/sarif-instance-detail.toml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/pytest.ini +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/build_container.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/scripts/run.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/session.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socket.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/alert_selection.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/cli_run.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/git_remote.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/log_uploader.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/pull_request.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/streaming.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/tools/reachability.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/fossa_compat.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/conftest.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_diff_scan_polling.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_facts_compression.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_full_scan_outputs.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_has_manifest_files.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_invalid_facts_marker.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/fixtures/simple-npm/package.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/reach-facts-probe.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/validate-gitlab.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/validate-json.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/validate-reachability.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/validate-sarif.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/e2e/validate-scan.sh +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/fixtures/fossa/README.md +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_alert_selection.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_cli_run.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_dependency_overview.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_disable_ignore.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_exclude_paths.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_fossa_compat.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_fossa_parity.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_git_remote.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_github_buildkite_config.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_gitlab_format.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_ignore_authorization.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_include_dirs.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_log_uploader.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_manifest_discovery.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_messages.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_pr_comment_rendering.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_pull_request_context.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_reachability.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_scan_scope.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_slack_plugin.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_slack_severity_normalization.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_socketcli.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_streaming.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/tests/unit/test_tier1_finalize.py +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/workflows/buildkite.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.9.7 → socketsecurity-2.10.0}/workflows/gitlab-ci.yml +0 -0
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.10.0
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- `--base-commit-sha` now sets the range changed-file detection reads, so a manifest
|
|
8
|
+
changed anywhere between that commit and HEAD triggers a comparison. Previously only
|
|
9
|
+
a recognized GitHub, GitLab, Bitbucket, or Buildkite pull request read a full range,
|
|
10
|
+
and every other run saw the current commit alone.
|
|
11
|
+
- A base commit that cannot be resolved in the checkout now warns instead of narrowing
|
|
12
|
+
the comparison to a single commit.
|
|
13
|
+
- A full scan reports its findings as repository findings rather than new ones, and
|
|
14
|
+
labels its link `Report Url`. The summary states why the counts do not affect the
|
|
15
|
+
exit code.
|
|
16
|
+
- Removed and replaced packages with a namespace no longer render a duplicated purl in
|
|
17
|
+
the dependency overview comment.
|
|
18
|
+
|
|
19
|
+
- The 200-character cap on the commit message now applies to the value read from the
|
|
20
|
+
repository, not only to `--commit-message`. A truncated message ends in `...` and the
|
|
21
|
+
truncation is reported at INFO.
|
|
22
|
+
- A full scan refused for its size (HTTP 413, 414 or 431) now distinguishes possible
|
|
23
|
+
upload-size and request-metadata causes and reports what to shorten.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `--ignore-commit-files` is documented as forcing a comparison, which is what it does.
|
|
28
|
+
The CLI reference and `--help` described it as forcing a full scan.
|
|
29
|
+
|
|
3
30
|
## 2.9.7
|
|
4
31
|
|
|
5
32
|
### Changed: bump pinned @coana-tech/cli to 15.10.51
|
|
@@ -256,7 +256,7 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
256
256
|
| `--commit-message` | False | *auto* | Commit message (auto-detected from git) |
|
|
257
257
|
| `--commit-sha` | False | *auto* | Commit SHA (auto-detected from git) |
|
|
258
258
|
| `--base-scan-id` | False | | Full scan ID to diff against, overriding the repository's head scan as the baseline. Mutually exclusive with `--base-commit-sha` |
|
|
259
|
-
| `--base-commit-sha`| False | | Commit SHA to prefer as the diff baseline, overriding the repository's head scan. The CLI uses its most recent matching full scan or the nearest scanned first-parent ancestor within 100 local commits. It errors (exit code 3, or `--exit-code-on-api-error`) if no scanned ancestor is reachable. Mutually exclusive with `--base-scan-id` |
|
|
259
|
+
| `--base-commit-sha`| False | | Commit SHA to prefer as the diff baseline, overriding the repository's head scan. The CLI uses its most recent matching full scan or the nearest scanned first-parent ancestor within 100 local commits. It errors (exit code 3, or `--exit-code-on-api-error`) if no scanned ancestor is reachable. Also sets the range changed-file detection reads, so a manifest changed anywhere between this commit and HEAD is seen. Mutually exclusive with `--base-scan-id` |
|
|
260
260
|
|
|
261
261
|
> **Diffing against the merge base** — by default, PR scans are diffed against the repository's latest matching head scan, which may include newer default-branch commits than your PR branched from. To prefer the commit your PR is based on, compute the merge base and pass it as the baseline:
|
|
262
262
|
>
|
|
@@ -267,6 +267,8 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
267
267
|
>
|
|
268
268
|
> `--base-commit-sha` does not create a scan of that commit. The CLI first looks for the newest non-temporary scan matching the repository, workspace, scan type, and exact commit. If the exact commit was not scanned, it walks up to 100 first-parent commits from that SHA in the local checkout and uses the nearest matching scanned ancestor. It logs a warning with the selected commit and distance because this produces a wider diff than the merge base.
|
|
269
269
|
>
|
|
270
|
+
> Supplying `--base-commit-sha` also widens the range the CLI reads when deciding whether any manifest changed. Without it, and outside a recognized CI pull request or merge request, the CLI sees only the current commit, so a pull request whose manifest changed in an earlier commit is treated as a source-only change and the comparison is skipped. If the base commit cannot be resolved in the local checkout, the CLI warns and falls back to the current commit alone; deepen the clone or fetch the base commit to compare the full range.
|
|
271
|
+
>
|
|
270
272
|
> Run `socketcli` regularly on the default branch so recent ancestors have scans. PR checkouts must also retain the merge base and enough first-parent history; shallow clones can shorten the search. Gaps are expected when CI cancels intermediate builds, commits use `[skip ci]`, pipelines are path-filtered, or the merge base predates your Socket rollout.
|
|
271
273
|
>
|
|
272
274
|
> If no scanned ancestor is reachable within the local 100-commit walk, the CLI **fails** (exit code 3, or your `--exit-code-on-api-error` value; exit 0 with `--disable-blocking`) instead of silently falling back to the repository head. API or permission failures also fail rather than being treated as a missing exact scan.
|
|
@@ -427,7 +429,7 @@ The launcher can be tuned via the `SOCKET_CLI_COANA_LAUNCHER` environment variab
|
|
|
427
429
|
#### Advanced Configuration
|
|
428
430
|
| Parameter | Required | Default | Description |
|
|
429
431
|
|:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
|
|
430
|
-
| `--ignore-commit-files` | False | False |
|
|
432
|
+
| `--ignore-commit-files` | False | False | Compare regardless of which files changed, scanning every manifest |
|
|
431
433
|
| `--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`. |
|
|
432
434
|
| `--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. See [Who can ignore an alert](#who-can-ignore-an-alert). |
|
|
433
435
|
| `--ignore-authorization` | False | enforce | Who may suppress alerts with `@SocketSecurity ignore`. `enforce` requires write access and honors the command with a warning when the provider cannot report it; `strict` rejects it in that case; `off` honors any commenter. See [Who can ignore an alert](#who-can-ignore-an-alert). |
|
|
@@ -674,10 +676,10 @@ The CLI now automatically detects repository information from your git environme
|
|
|
674
676
|
- **Commit message**: Latest commit message
|
|
675
677
|
- **Committer information**: Git commit author details
|
|
676
678
|
- **Default branch status**: Determined from git repository and CI environment
|
|
677
|
-
- **Changed files**: Files modified in the current commit (for differential scanning)
|
|
679
|
+
- **Changed files**: Files modified in the current commit, or across the whole `--base-commit-sha`..HEAD range when a base commit is supplied (for differential scanning)
|
|
678
680
|
> **Note on merge commits**:
|
|
679
681
|
> Standard merges (two parents) are supported.
|
|
680
|
-
> For *octopus merges* (three or more parents), Git only reports changes relative to the first parent. This can lead to incomplete or empty file lists if changes only exist relative to other parents. In these cases, differential scanning may be skipped. To ensure coverage, use `--ignore-commit-files` to
|
|
682
|
+
> For *octopus merges* (three or more parents), Git only reports changes relative to the first parent. This can lead to incomplete or empty file lists if changes only exist relative to other parents. In these cases, differential scanning may be skipped. To ensure coverage, use `--ignore-commit-files` to compare regardless of the detected changes, or specify files explicitly with `--files`.
|
|
681
683
|
### Default Branch Detection
|
|
682
684
|
|
|
683
685
|
The CLI uses intelligent default branch detection with the following priority:
|
|
@@ -728,11 +730,11 @@ GitLab token/auth behavior and CI examples are documented in [`ci-cd.md`](ci-cd.
|
|
|
728
730
|
|
|
729
731
|
The CLI determines which files to scan based on the following logic:
|
|
730
732
|
|
|
731
|
-
1. **Git Commit Files (Default)**: The CLI automatically checks files changed
|
|
733
|
+
1. **Git Commit Files (Default)**: The CLI automatically checks which files changed. In a recognized CI pull request or merge request, and whenever `--base-commit-sha` is supplied, that is every file changed across the range; otherwise it is the current commit alone. If any of them match supported manifest patterns (like package.json, requirements.txt, etc.), a comparison is run.
|
|
732
734
|
|
|
733
735
|
2. **`--files` Parameter Override**: When specified, this parameter takes precedence over git commit detection. It accepts a JSON array of file paths to check for manifest files.
|
|
734
736
|
|
|
735
|
-
3. **`--ignore-commit-files` Flag**: When set,
|
|
737
|
+
3. **`--ignore-commit-files` Flag**: When set, the changed-file check is skipped entirely. The CLI runs the comparison regardless of what changed, and scans every manifest file in the target directory.
|
|
736
738
|
|
|
737
739
|
4. **Automatic Fallback**: If no manifest files are found in git commit changes and no `--files` are specified, the CLI automatically switches to "API mode" and performs a full repository scan.
|
|
738
740
|
|
|
@@ -742,7 +744,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
742
744
|
|
|
743
745
|
- **Differential Mode**: When manifest files are detected in changes, performs a diff scan with PR/MR comment integration
|
|
744
746
|
- **API Mode**: When no manifest files are in changes, creates a full scan report without PR comments but still scans the entire repository
|
|
745
|
-
- **Force Mode**: With `--ignore-commit-files`, always
|
|
747
|
+
- **Force Mode**: With `--ignore-commit-files`, always runs a comparison regardless of which files changed, over every manifest in the target path
|
|
746
748
|
- **Forced Diff Mode**: With `--enable-diff`, forces differential mode even when using `--integration api` (without SCM integration)
|
|
747
749
|
|
|
748
750
|
### Examples
|
|
@@ -750,7 +752,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
750
752
|
- **Commit with manifest file**: If your commit includes changes to `package.json`, a differential scan will be triggered automatically with PR comment integration.
|
|
751
753
|
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), the CLI automatically switches to API mode and performs a full repository scan.
|
|
752
754
|
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before determining scan type.
|
|
753
|
-
- **Using `--ignore-commit-files`**: This
|
|
755
|
+
- **Using `--ignore-commit-files`**: This runs the comparison regardless of what's in your commit, over all manifest files in the target path. Use it when the changed-file check would otherwise skip a comparison you need.
|
|
754
756
|
- **Using `--enable-diff`**: Forces diff mode without SCM integration - useful when you want differential scanning but are using `--integration api`. For example: `socketcli --integration api --enable-diff --target-path /path/to/repo`
|
|
755
757
|
- **Auto-detection**: Most CI/CD scenarios now work with just `socketcli --target-path /path/to/repo --scm github --pr-number $PR_NUM`
|
|
756
758
|
|
|
@@ -19,6 +19,32 @@ def get_plugin_config_from_env(prefix: str) -> dict:
|
|
|
19
19
|
return {}
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
# commit_message rides in the query string of POST /v0/orgs/{org}/full-scans, so an
|
|
23
|
+
# oversized message overflows the edge proxy's request line limit before the API ever
|
|
24
|
+
# sees it. The API itself has no length validation on the field; the rejection comes
|
|
25
|
+
# from the proxy, which reports 413, 414 or 431 depending on which limit it checks. 200
|
|
26
|
+
# chars is a conservative ceiling given URL encoding can 2-3x the raw character count.
|
|
27
|
+
MAX_COMMIT_MESSAGE_LENGTH = 200
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
COMMIT_MESSAGE_TRUNCATION_MARKER = "..."
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def truncate_commit_message(commit_message: Optional[str]) -> Optional[str]:
|
|
34
|
+
"""Cap commit_message to a length the full-scan request line can carry."""
|
|
35
|
+
if commit_message and len(commit_message) > MAX_COMMIT_MESSAGE_LENGTH:
|
|
36
|
+
# INFO, not DEBUG: the scan keeps the truncated value, so for a CI job that does
|
|
37
|
+
# not pass --enable-debug this line is the only explanation of why the message in
|
|
38
|
+
# the dashboard is clipped.
|
|
39
|
+
logging.info(
|
|
40
|
+
f"commit_message truncated from {len(commit_message)} to "
|
|
41
|
+
f"{MAX_COMMIT_MESSAGE_LENGTH} characters to stay within API request size limits"
|
|
42
|
+
)
|
|
43
|
+
keep = MAX_COMMIT_MESSAGE_LENGTH - len(COMMIT_MESSAGE_TRUNCATION_MARKER)
|
|
44
|
+
return commit_message[:keep] + COMMIT_MESSAGE_TRUNCATION_MARKER
|
|
45
|
+
return commit_message
|
|
46
|
+
|
|
47
|
+
|
|
22
48
|
def load_cli_config_file(config_path: str) -> dict:
|
|
23
49
|
"""
|
|
24
50
|
Load CLI defaults from a JSON or TOML file.
|
|
@@ -201,7 +227,13 @@ class CliConfig:
|
|
|
201
227
|
legal: bool = False
|
|
202
228
|
legal_format: str = "socket"
|
|
203
229
|
config_file: Optional[str] = None
|
|
204
|
-
|
|
230
|
+
|
|
231
|
+
def __post_init__(self):
|
|
232
|
+
# Capped on construction so that every source of commit_message -- the
|
|
233
|
+
# --commit-message flag, a config file, the git backfill in socketcli -- lands
|
|
234
|
+
# under the limit.
|
|
235
|
+
self.commit_message = truncate_commit_message(self.commit_message)
|
|
236
|
+
|
|
205
237
|
@classmethod
|
|
206
238
|
def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
|
|
207
239
|
parser = create_argument_parser()
|
|
@@ -257,19 +289,6 @@ class CliConfig:
|
|
|
257
289
|
if commit_message and commit_message.startswith('"') and commit_message.endswith('"'):
|
|
258
290
|
commit_message = commit_message[1:-1]
|
|
259
291
|
|
|
260
|
-
# Truncate to avoid 413s from oversized URL query parameters.
|
|
261
|
-
# The API has no application-layer length validation on commit_message;
|
|
262
|
-
# the 413 originates from an infrastructure-layer URL length limit
|
|
263
|
-
# (nginx/Cloudflare). 200 chars chosen as a conservative ceiling given
|
|
264
|
-
# URL encoding can 2-3x raw character count.
|
|
265
|
-
MAX_COMMIT_MESSAGE_LENGTH = 200
|
|
266
|
-
if commit_message and len(commit_message) > MAX_COMMIT_MESSAGE_LENGTH:
|
|
267
|
-
logging.debug(
|
|
268
|
-
f"commit_message truncated from {len(commit_message)} to "
|
|
269
|
-
f"{MAX_COMMIT_MESSAGE_LENGTH} characters to avoid API request size limits"
|
|
270
|
-
)
|
|
271
|
-
commit_message = commit_message[:MAX_COMMIT_MESSAGE_LENGTH]
|
|
272
|
-
|
|
273
292
|
config_args = {
|
|
274
293
|
'api_token': api_token,
|
|
275
294
|
'repo': args.repo,
|
|
@@ -612,7 +631,9 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
612
631
|
help="Commit SHA to diff the new scan against, overriding the repository's head "
|
|
613
632
|
"scan as the baseline. The CLI uses the most recent matching full scan, or "
|
|
614
633
|
"the nearest scanned first-parent ancestor within 100 local commits when "
|
|
615
|
-
"the commit itself was not scanned.
|
|
634
|
+
"the commit itself was not scanned. Also sets the range changed-file "
|
|
635
|
+
"detection reads, so a manifest changed anywhere between this commit and "
|
|
636
|
+
"HEAD is seen. Mutually exclusive with --base-scan-id."
|
|
616
637
|
)
|
|
617
638
|
|
|
618
639
|
# Path and File options
|
|
@@ -913,7 +934,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
913
934
|
"--ignore-commit-files",
|
|
914
935
|
dest="ignore_commit_files",
|
|
915
936
|
action="store_true",
|
|
916
|
-
help="
|
|
937
|
+
help="Compare against the baseline regardless of which files changed, scanning every manifest in the target path"
|
|
917
938
|
)
|
|
918
939
|
advanced_group.add_argument(
|
|
919
940
|
"--ignore_commit_files",
|
|
@@ -113,6 +113,12 @@ FULL_SCAN_UPLOAD_BACKOFF_SCHEDULE_SECONDS = (10.0, 30.0, None)
|
|
|
113
113
|
FULL_SCAN_UPLOAD_MAX_ATTEMPTS = len(FULL_SCAN_UPLOAD_BACKOFF_SCHEDULE_SECONDS)
|
|
114
114
|
FULL_SCAN_UPLOAD_BACKOFF_JITTER_SECONDS = 2.0
|
|
115
115
|
|
|
116
|
+
# Statuses that mean the request is too large to process. Scan metadata travels in the
|
|
117
|
+
# query string of the full-scan POST, while manifests travel in its multipart body. A
|
|
118
|
+
# 413 can refer to either part; 414 points to the URL, and some proxies report 431 when
|
|
119
|
+
# the encoded request target exceeds their header limit. None are transient.
|
|
120
|
+
REQUEST_TOO_LARGE_STATUS_CODES = (413, 414, 431)
|
|
121
|
+
|
|
116
122
|
# Diff-scan polling policy. The legacy scan comparison (fullscans.stream_diff) holds a
|
|
117
123
|
# single HTTP connection open, fully idle, while the backend computes the diff; network
|
|
118
124
|
# middleboxes with TCP idle timeouts (notably Azure NAT gateways, which default to
|
|
@@ -1118,6 +1124,23 @@ class Core:
|
|
|
1118
1124
|
res = self.sdk.fullscans.post(upload_files, params, use_types=True, use_lazy_loading=True, max_open_files=50, base_paths=base_paths)
|
|
1119
1125
|
break
|
|
1120
1126
|
except APIFailure as error:
|
|
1127
|
+
if error.status_code in REQUEST_TOO_LARGE_STATUS_CODES:
|
|
1128
|
+
if error.status_code == 413:
|
|
1129
|
+
guidance = (
|
|
1130
|
+
"The response does not distinguish between an oversized multipart "
|
|
1131
|
+
"upload and oversized scan metadata in the request URL. Reduce the "
|
|
1132
|
+
"uploaded scan inputs, or pass a shorter --commit-message."
|
|
1133
|
+
)
|
|
1134
|
+
else:
|
|
1135
|
+
guidance = (
|
|
1136
|
+
"Scan metadata is sent in the request URL. Pass a shorter "
|
|
1137
|
+
"--commit-message or shorten other scan metadata."
|
|
1138
|
+
)
|
|
1139
|
+
raise APIFailure(
|
|
1140
|
+
f"Full scan request rejected as too large (HTTP {error.status_code}). "
|
|
1141
|
+
f"{guidance}\n{error}",
|
|
1142
|
+
status_code=error.status_code,
|
|
1143
|
+
) from error
|
|
1121
1144
|
if backoff_seconds is None or not error.is_transient_error():
|
|
1122
1145
|
raise
|
|
1123
1146
|
wait_seconds = backoff_seconds + random.uniform(
|
|
@@ -2267,8 +2290,6 @@ class Core:
|
|
|
2267
2290
|
try:
|
|
2268
2291
|
pkg = Package.from_diff_artifact(asdict(artifact))
|
|
2269
2292
|
pkg = Core.update_package_values(pkg)
|
|
2270
|
-
if pkg.namespace:
|
|
2271
|
-
pkg.purl += f"{pkg.namespace}/{pkg.purl}"
|
|
2272
2293
|
removed_packages[artifact.id] = pkg
|
|
2273
2294
|
except KeyError:
|
|
2274
2295
|
log.error(f"KeyError: Could not create package from removed artifact {artifact.id}")
|
|
@@ -11,10 +11,21 @@ from socketsecurity.core import log
|
|
|
11
11
|
class Git:
|
|
12
12
|
repo: Repo
|
|
13
13
|
path: str
|
|
14
|
+
base_commit_sha: str | None
|
|
14
15
|
|
|
15
|
-
def __init__(self, path: str):
|
|
16
|
+
def __init__(self, path: str, base_commit_sha: str | None = None):
|
|
17
|
+
"""
|
|
18
|
+
Reads the repository state a scan is built from.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
path: Path to the repository working tree
|
|
22
|
+
base_commit_sha: Commit the comparison should start from. Supplied when
|
|
23
|
+
the caller knows the range and no CI environment describes it, which
|
|
24
|
+
is the only way changed-file detection can see commits behind HEAD.
|
|
25
|
+
"""
|
|
16
26
|
initialization_start = time.perf_counter()
|
|
17
27
|
self.path = path
|
|
28
|
+
self.base_commit_sha = base_commit_sha
|
|
18
29
|
self._fetched_ref_commits = {}
|
|
19
30
|
self.ensure_safe_directory(path)
|
|
20
31
|
self.repo = Repo(path)
|
|
@@ -164,40 +175,62 @@ class Git:
|
|
|
164
175
|
buildkite_pr = os.getenv('BUILDKITE_PULL_REQUEST')
|
|
165
176
|
buildkite_base_ref = os.getenv('BUILDKITE_PULL_REQUEST_BASE_BRANCH')
|
|
166
177
|
buildkite_head_ref = os.getenv('BUILDKITE_BRANCH')
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
head_ref=buildkite_head_ref,
|
|
172
|
-
)
|
|
173
|
-
if detected:
|
|
174
|
-
detection_source = "buildkite-pr"
|
|
175
|
-
elif github_event_name == 'pull_request' and github_base_ref:
|
|
178
|
+
|
|
179
|
+
# An explicitly supplied base commit states the comparison range outright,
|
|
180
|
+
# so it is honored before any inference from CI environment variables.
|
|
181
|
+
if self.base_commit_sha:
|
|
176
182
|
detected = self._detect_pull_request_changes(
|
|
177
|
-
provider="
|
|
178
|
-
base_ref=
|
|
179
|
-
head_ref=
|
|
183
|
+
provider="explicit base commit",
|
|
184
|
+
base_ref=self.base_commit_sha,
|
|
185
|
+
head_ref=None,
|
|
186
|
+
use_merge_base=False,
|
|
180
187
|
)
|
|
181
188
|
if detected:
|
|
182
|
-
detection_source = "
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
189
|
+
detection_source = "explicit-base-commit"
|
|
190
|
+
else:
|
|
191
|
+
log.warning(
|
|
192
|
+
f"Could not resolve base commit {self.base_commit_sha} in this "
|
|
193
|
+
"checkout, so changed-file detection falls back to the current "
|
|
194
|
+
"commit alone. A manifest changed earlier in the range will not "
|
|
195
|
+
"be seen, which can skip the comparison entirely. Deepen the "
|
|
196
|
+
"clone or fetch the base commit to compare the full range."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if not detected:
|
|
200
|
+
if self._is_buildkite_pull_request(buildkite_pr) and buildkite_base_ref:
|
|
201
|
+
detected = self._detect_pull_request_changes(
|
|
202
|
+
provider="Buildkite",
|
|
203
|
+
base_ref=buildkite_base_ref,
|
|
204
|
+
head_ref=buildkite_head_ref,
|
|
205
|
+
)
|
|
206
|
+
if detected:
|
|
207
|
+
detection_source = "buildkite-pr"
|
|
208
|
+
elif github_event_name == 'pull_request' and github_base_ref:
|
|
209
|
+
detected = self._detect_pull_request_changes(
|
|
210
|
+
provider="GitHub",
|
|
211
|
+
base_ref=github_base_ref,
|
|
212
|
+
head_ref=github_head_ref,
|
|
213
|
+
)
|
|
214
|
+
if detected:
|
|
215
|
+
detection_source = "github-pr"
|
|
216
|
+
# Commits to default branch (push events)
|
|
217
|
+
elif github_event_name == 'push' and github_before_sha and github_sha:
|
|
218
|
+
try:
|
|
219
|
+
diff_files = self.repo.git.diff('--name-only', f'{github_before_sha}..{github_sha}')
|
|
220
|
+
self.show_files = diff_files.splitlines()
|
|
221
|
+
log.debug(f"Changed files detected via git diff (GitHub push): {self.show_files}")
|
|
222
|
+
detected = True
|
|
223
|
+
detection_source = "github-push"
|
|
224
|
+
except Exception as error:
|
|
225
|
+
log.debug(f"Failed to get changed files via git diff (GitHub push): {error}")
|
|
226
|
+
elif github_event_name == 'push':
|
|
227
|
+
try:
|
|
228
|
+
self.show_files = self.repo.git.show(self.commit, name_only=True, format="%n").splitlines()
|
|
229
|
+
log.debug(f"Changed files detected via git show (GitHub push fallback): {self.show_files}")
|
|
230
|
+
detected = True
|
|
231
|
+
detection_source = "github-push-fallback"
|
|
232
|
+
except Exception as error:
|
|
233
|
+
log.debug(f"Failed to get changed files via git show (GitHub push fallback): {error}")
|
|
201
234
|
# GitLab CI Merge Request context
|
|
202
235
|
if not detected:
|
|
203
236
|
gitlab_target = os.getenv('CI_MERGE_REQUEST_TARGET_BRANCH_NAME')
|
|
@@ -316,8 +349,9 @@ class Git:
|
|
|
316
349
|
provider: str,
|
|
317
350
|
base_ref: str,
|
|
318
351
|
head_ref: str | None,
|
|
352
|
+
use_merge_base: bool = True,
|
|
319
353
|
) -> bool:
|
|
320
|
-
"""Detect a
|
|
354
|
+
"""Detect a base-to-head range locally, fetching only refs needed to complete it."""
|
|
321
355
|
base_commit = self._resolve_ref(base_ref)
|
|
322
356
|
if base_commit is None:
|
|
323
357
|
base_commit = self._fetch_ref(base_ref, f"{provider} pull-request base ref missing")
|
|
@@ -326,7 +360,8 @@ class Git:
|
|
|
326
360
|
return False
|
|
327
361
|
|
|
328
362
|
head_commit = self.commit.hexsha
|
|
329
|
-
|
|
363
|
+
range_separator = "..." if use_merge_base else ".."
|
|
364
|
+
diff_range = f"{base_commit}{range_separator}{head_commit}"
|
|
330
365
|
try:
|
|
331
366
|
diff_files = self.repo.git.diff("--name-only", diff_range)
|
|
332
367
|
self.show_files = diff_files.splitlines()
|
|
@@ -352,7 +387,7 @@ class Git:
|
|
|
352
387
|
try:
|
|
353
388
|
diff_files = self.repo.git.diff(
|
|
354
389
|
"--name-only",
|
|
355
|
-
f"{base_commit}
|
|
390
|
+
f"{base_commit}{range_separator}{head_commit}",
|
|
356
391
|
)
|
|
357
392
|
self.show_files = diff_files.splitlines()
|
|
358
393
|
log.debug(
|
|
@@ -229,10 +229,8 @@ class OutputHandler:
|
|
|
229
229
|
|
|
230
230
|
def build_summary_text(self, diff_report: Diff) -> str:
|
|
231
231
|
"""Render the console summary text for stdout and file output."""
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
and not getattr(diff_report, "alerts_fetched", False)
|
|
235
|
-
):
|
|
232
|
+
is_full_scan = getattr(diff_report, "is_full_scan", False)
|
|
233
|
+
if is_full_scan and not getattr(diff_report, "alerts_fetched", False):
|
|
236
234
|
lines = ["Full scan completed. Findings were not fetched for console output."]
|
|
237
235
|
report_link = getattr(diff_report, "report_url", "") or getattr(
|
|
238
236
|
diff_report, "diff_url", ""
|
|
@@ -264,20 +262,33 @@ class OutputHandler:
|
|
|
264
262
|
selected_diff = clone_diff_with_selected_alerts(diff_report, selected_alerts)
|
|
265
263
|
console_security_comment = Messages.create_console_security_alert_table(selected_diff)
|
|
266
264
|
|
|
265
|
+
# A full scan has no baseline, so everything it carries is a finding in the
|
|
266
|
+
# repository rather than one the change introduced. Calling those NEW would
|
|
267
|
+
# also contradict the exit code, which stays 0 because no finding can be
|
|
268
|
+
# attributed to the change.
|
|
269
|
+
blocking_label = "Blocking issues" if is_full_scan else "NEW blocking issues"
|
|
270
|
+
warning_label = "Warning issues" if is_full_scan else "NEW warning issues"
|
|
271
|
+
link_label = "Report Url" if is_full_scan else "Diff Url"
|
|
272
|
+
|
|
267
273
|
lines = ["Security issues detected by Socket Security:"]
|
|
268
274
|
if new_blocking > 0:
|
|
269
|
-
lines.append(f" -
|
|
275
|
+
lines.append(f" - {blocking_label}: {new_blocking}")
|
|
270
276
|
if new_warning > 0:
|
|
271
|
-
lines.append(f" -
|
|
277
|
+
lines.append(f" - {warning_label}: {new_warning}")
|
|
272
278
|
if unchanged_blocking > 0:
|
|
273
279
|
lines.append(
|
|
274
280
|
f" - EXISTING blocking issues: {unchanged_blocking} (causing failure due to --strict-blocking)"
|
|
275
281
|
)
|
|
276
282
|
if unchanged_warning > 0:
|
|
277
283
|
lines.append(f" - EXISTING warning issues: {unchanged_warning}")
|
|
284
|
+
if is_full_scan:
|
|
285
|
+
lines.append(
|
|
286
|
+
" Reported against the whole repository, with no baseline to compare "
|
|
287
|
+
"against, so these do not affect the exit code."
|
|
288
|
+
)
|
|
278
289
|
|
|
279
290
|
report_link = getattr(diff_report, "report_url", "") or getattr(diff_report, "diff_url", "")
|
|
280
|
-
lines.append(f"
|
|
291
|
+
lines.append(f"{link_label}: {report_link}")
|
|
281
292
|
lines.append("")
|
|
282
293
|
lines.append(str(console_security_comment))
|
|
283
294
|
return "\n".join(lines)
|
|
@@ -12,7 +12,7 @@ from git import InvalidGitRepositoryError, NoSuchPathError
|
|
|
12
12
|
from socketdev import socketdev
|
|
13
13
|
from socketdev.fullscans import FullScanParams
|
|
14
14
|
|
|
15
|
-
from socketsecurity.config import CliConfig
|
|
15
|
+
from socketsecurity.config import CliConfig, truncate_commit_message
|
|
16
16
|
from socketsecurity.core import Core
|
|
17
17
|
from socketsecurity.core.classes import Diff
|
|
18
18
|
from socketsecurity.core.cli_client import CliClient
|
|
@@ -210,6 +210,35 @@ def create_scm_scan(
|
|
|
210
210
|
return diff, False
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
def apply_git_context(config: CliConfig) -> Tuple[bool, Optional[Git]]:
|
|
214
|
+
"""
|
|
215
|
+
Fill in any repo details the caller did not pass from the checkout at target_path.
|
|
216
|
+
|
|
217
|
+
Returns whether target_path is a git repository, along with the Git handle when it is.
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
git_repo = Git(config.target_path, base_commit_sha=config.base_commit_sha)
|
|
221
|
+
except InvalidGitRepositoryError:
|
|
222
|
+
log.debug("Not a git repository, setting ignore_commit_files=True")
|
|
223
|
+
config.ignore_commit_files = True
|
|
224
|
+
return False, None
|
|
225
|
+
except NoSuchPathError:
|
|
226
|
+
raise Exception(f"Unable to find path {config.target_path}")
|
|
227
|
+
|
|
228
|
+
if not config.repo:
|
|
229
|
+
config.repo = git_repo.repo_name
|
|
230
|
+
if not config.commit_sha:
|
|
231
|
+
config.commit_sha = git_repo.commit_str
|
|
232
|
+
if not config.branch:
|
|
233
|
+
config.branch = git_repo.branch
|
|
234
|
+
if not config.committers:
|
|
235
|
+
config.committers = [git_repo.get_formatted_committer()]
|
|
236
|
+
if not config.commit_message:
|
|
237
|
+
# A repository's commit message is unbounded and ships in the query string.
|
|
238
|
+
config.commit_message = truncate_commit_message(git_repo.commit_message)
|
|
239
|
+
return True, git_repo
|
|
240
|
+
|
|
241
|
+
|
|
213
242
|
def build_socket_sdk(config: CliConfig) -> socketdev:
|
|
214
243
|
cli_user_agent_string = f"SocketPythonCLI/{config.version}"
|
|
215
244
|
return socketdev(
|
|
@@ -402,27 +431,7 @@ def main_code():
|
|
|
402
431
|
discovered_scan_files = None
|
|
403
432
|
|
|
404
433
|
# Git setup
|
|
405
|
-
is_repo =
|
|
406
|
-
git_repo: Git
|
|
407
|
-
try:
|
|
408
|
-
git_repo = Git(config.target_path)
|
|
409
|
-
is_repo = True
|
|
410
|
-
if not config.repo:
|
|
411
|
-
config.repo = git_repo.repo_name
|
|
412
|
-
if not config.commit_sha:
|
|
413
|
-
config.commit_sha = git_repo.commit_str
|
|
414
|
-
if not config.branch:
|
|
415
|
-
config.branch = git_repo.branch
|
|
416
|
-
if not config.committers:
|
|
417
|
-
config.committers = [git_repo.get_formatted_committer()]
|
|
418
|
-
if not config.commit_message:
|
|
419
|
-
config.commit_message = git_repo.commit_message
|
|
420
|
-
except InvalidGitRepositoryError:
|
|
421
|
-
is_repo = False
|
|
422
|
-
log.debug("Not a git repository, setting ignore_commit_files=True")
|
|
423
|
-
config.ignore_commit_files = True
|
|
424
|
-
except NoSuchPathError:
|
|
425
|
-
raise Exception(f"Unable to find path {config.target_path}")
|
|
434
|
+
is_repo, git_repo = apply_git_context(config)
|
|
426
435
|
|
|
427
436
|
# Track whether repo/branch fell back to the default sentinels so reachability can skip
|
|
428
437
|
# forwarding them as coana cache-bucket keys (computed before any workspace suffixing).
|
|
@@ -3,7 +3,7 @@ from dataclasses import asdict, fields
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
|
-
from socketdev.fullscans import DiffArtifact
|
|
6
|
+
from socketdev.fullscans import DiffArtifact, StreamDiffResponse
|
|
7
7
|
|
|
8
8
|
from socketsecurity.core import Core
|
|
9
9
|
from socketsecurity.core.classes import Package
|
|
@@ -312,3 +312,48 @@ def print_added_and_removed(added, removed):
|
|
|
312
312
|
# pkg1_purl = next(p for p in diff.new_packages if p.id == "pkg1")
|
|
313
313
|
# assert hasattr(pkg1_purl, "capabilities")
|
|
314
314
|
# assert set(pkg1_purl.capabilities) == {"File System Access", "Network Access"}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _namespaced_diff_response(namespace: str = "com.example"):
|
|
318
|
+
"""One namespaced artifact, delivered as both an addition and a removal."""
|
|
319
|
+
raw = json.loads(
|
|
320
|
+
(Path(__file__).parent.parent / "data/fullscans/diff/stream_diff.json").read_text()
|
|
321
|
+
)
|
|
322
|
+
template = raw["data"]["artifacts"]["added"][0]
|
|
323
|
+
artifacts = {bucket: [] for bucket in ("added", "removed", "unchanged", "replaced", "updated")}
|
|
324
|
+
for bucket in ("added", "removed"):
|
|
325
|
+
artifacts[bucket].append(
|
|
326
|
+
dict(
|
|
327
|
+
template,
|
|
328
|
+
diffType=bucket,
|
|
329
|
+
head=None,
|
|
330
|
+
base=None,
|
|
331
|
+
id=f"namespaced-{bucket}",
|
|
332
|
+
namespace=namespace,
|
|
333
|
+
name="widget",
|
|
334
|
+
version="1.0.0",
|
|
335
|
+
type="maven",
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
return StreamDiffResponse.from_dict({
|
|
339
|
+
"success": raw["success"],
|
|
340
|
+
"status": raw["status"],
|
|
341
|
+
"data": {**raw["data"], "artifacts": artifacts},
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def test_removed_package_purl_matches_the_added_form(core):
|
|
346
|
+
"""A namespace belongs in the purl once, whichever bucket the artifact arrives in.
|
|
347
|
+
|
|
348
|
+
The purl reaches the dependency overview comment verbatim, so a second copy of
|
|
349
|
+
the namespace renders as an unreadable package name on every removed or
|
|
350
|
+
replaced row.
|
|
351
|
+
"""
|
|
352
|
+
core.sdk.fullscans.stream_diff.side_effect = None
|
|
353
|
+
core.sdk.fullscans.stream_diff.return_value = _namespaced_diff_response()
|
|
354
|
+
core.sdk.diffscans.create_from_ids.side_effect = Exception("diff-scans unavailable")
|
|
355
|
+
|
|
356
|
+
added, removed, _ = core.get_added_and_removed_packages("head", "new")
|
|
357
|
+
|
|
358
|
+
assert added["namespaced-added"].purl == "com.example/widget@1.0.0"
|
|
359
|
+
assert removed["namespaced-removed"].purl == added["namespaced-added"].purl
|
|
@@ -31,14 +31,15 @@ class TestCommitMessageTruncation:
|
|
|
31
31
|
config = CliConfig.from_args(
|
|
32
32
|
["--api-token", "test", "--commit-message", "a" * 250]
|
|
33
33
|
)
|
|
34
|
-
assert config.commit_message == "a" *
|
|
34
|
+
assert config.commit_message == "a" * 197 + "..."
|
|
35
|
+
assert len(config.commit_message) == 200
|
|
35
36
|
|
|
36
37
|
def test_quote_strip_runs_before_truncation(self):
|
|
37
38
|
quoted = '"' + ("b" * 250) + '"'
|
|
38
39
|
config = CliConfig.from_args(
|
|
39
40
|
["--api-token", "test", "--commit-message", quoted]
|
|
40
41
|
)
|
|
41
|
-
assert config.commit_message == "b" *
|
|
42
|
+
assert config.commit_message == "b" * 197 + "..."
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
class TestCliConfig:
|