socketsecurity 2.4.20__tar.gz → 2.5.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.
Files changed (152) hide show
  1. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/CHANGELOG.md +22 -0
  2. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/PKG-INFO +25 -3
  3. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/README.md +24 -2
  4. socketsecurity-2.5.0/docs/ci-cd.md +167 -0
  5. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/docs/cli-reference.md +34 -0
  6. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/pyproject.toml +1 -1
  7. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/__init__.py +1 -1
  8. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/config.py +29 -0
  9. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/__init__.py +94 -8
  10. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_sdk_methods.py +132 -1
  11. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_config.py +51 -0
  12. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/uv.lock +1 -1
  13. socketsecurity-2.4.20/docs/ci-cd.md +0 -119
  14. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/CODEOWNERS +0 -0
  15. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  16. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  17. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  18. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/actions/setup-docker/action.yml +0 -0
  20. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/actions/setup-hatch/action.yml +0 -0
  21. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/actions/setup-sfw/action.yml +0 -0
  22. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/dependabot.yml +0 -0
  23. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/dependency-review.yml +0 -0
  24. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/docker-stable.yml +0 -0
  25. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/e2e-test.yml +0 -0
  26. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/pr-preview.yml +0 -0
  27. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/python-tests.yml +0 -0
  28. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/release.yml +0 -0
  29. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/workflows/version-check.yml +0 -0
  30. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.github/zizmor.yml +0 -0
  31. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.gitignore +0 -0
  32. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.hooks/sync_version.py +0 -0
  33. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.pre-commit-config.yaml +0 -0
  34. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/.python-version +0 -0
  35. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/Dockerfile +0 -0
  36. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/LICENSE +0 -0
  37. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/Makefile +0 -0
  38. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/docs/development.md +0 -0
  39. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/docs/troubleshooting.md +0 -0
  40. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-dashboard-parity.json +0 -0
  41. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-dashboard-parity.toml +0 -0
  42. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-diff-ci-cd.json +0 -0
  43. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-diff-ci-cd.toml +0 -0
  44. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-instance-detail.json +0 -0
  45. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/examples/config/sarif-instance-detail.toml +0 -0
  46. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/instructions/gitlab-commit-status/uat.md +0 -0
  47. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/pytest.ini +0 -0
  48. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/build_container.sh +0 -0
  49. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/build_container_flexible.sh +0 -0
  50. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/deploy-test-docker.sh +0 -0
  51. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/deploy-test-pypi.sh +0 -0
  52. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/docker-entrypoint.sh +0 -0
  53. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/scripts/run.sh +0 -0
  54. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/session.md +0 -0
  55. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socket.yml +0 -0
  56. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/alert_selection.py +0 -0
  57. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/classes.py +0 -0
  58. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/cli_client.py +0 -0
  59. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/cli_run.py +0 -0
  60. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/exceptions.py +0 -0
  61. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/git_interface.py +0 -0
  62. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/helper/__init__.py +0 -0
  63. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  64. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/lazy_file_loader.py +0 -0
  65. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/log_uploader.py +0 -0
  66. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/logging.py +0 -0
  67. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/messages.py +0 -0
  68. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/resource_utils.py +0 -0
  69. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm/__init__.py +0 -0
  70. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm/base.py +0 -0
  71. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm/client.py +0 -0
  72. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm/github.py +0 -0
  73. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm/gitlab.py +0 -0
  74. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/scm_comments.py +0 -0
  75. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/socket_config.py +0 -0
  76. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/streaming.py +0 -0
  77. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/tools/reachability.py +0 -0
  78. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/core/utils.py +0 -0
  79. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/fossa_compat.py +0 -0
  80. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/output.py +0 -0
  81. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/__init__.py +0 -0
  82. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/base.py +0 -0
  83. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/formatters/__init__.py +0 -0
  84. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/formatters/slack.py +0 -0
  85. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/jira.py +0 -0
  86. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/manager.py +0 -0
  87. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/slack.py +0 -0
  88. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/teams.py +0 -0
  89. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/plugins/webhook.py +0 -0
  90. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/socketsecurity/socketcli.py +0 -0
  91. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/__init__.py +0 -0
  92. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/conftest.py +0 -0
  93. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/create_diff_input.json +0 -0
  94. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_diff_alerts.py +0 -0
  95. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_diff_generation.py +0 -0
  96. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_facts_compression.py +0 -0
  97. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_has_manifest_files.py +0 -0
  98. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_package_and_alerts.py +0 -0
  99. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/core/test_supporting_methods.py +0 -0
  100. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/create_response.json +0 -0
  101. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/diff/stream_diff.json +0 -0
  102. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  103. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/head_scan/metadata.json +0 -0
  104. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  105. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  106. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/new_scan/metadata.json +0 -0
  107. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  108. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/repos/repo_info_error.json +0 -0
  109. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/repos/repo_info_no_head.json +0 -0
  110. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/repos/repo_info_success.json +0 -0
  111. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/data/settings/security-policy.json +0 -0
  112. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  113. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  114. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  115. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/validate-gitlab.sh +0 -0
  116. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/validate-json.sh +0 -0
  117. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/validate-reachability.sh +0 -0
  118. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/validate-sarif.sh +0 -0
  119. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/e2e/validate-scan.sh +0 -0
  120. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/fixtures/fossa/README.md +0 -0
  121. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  122. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  123. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  124. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  125. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/__init__.py +0 -0
  126. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_alert_selection.py +0 -0
  127. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_cli_config.py +0 -0
  128. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_cli_run.py +0 -0
  129. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_client.py +0 -0
  130. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_dependency_overview.py +0 -0
  131. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_disable_ignore.py +0 -0
  132. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_exclude_paths.py +0 -0
  133. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_fossa_compat.py +0 -0
  134. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_fossa_parity.py +0 -0
  135. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_full_scan_retry.py +0 -0
  136. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_gitlab_auth.py +0 -0
  137. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  138. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_gitlab_commit_status.py +0 -0
  139. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_gitlab_format.py +0 -0
  140. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  141. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_include_dirs.py +0 -0
  142. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_log_uploader.py +0 -0
  143. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_output.py +0 -0
  144. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_reachability.py +0 -0
  145. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_slack_plugin.py +0 -0
  146. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_socketcli.py +0 -0
  147. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_streaming.py +0 -0
  148. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/tests/unit/test_tier1_finalize.py +0 -0
  149. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/workflows/bitbucket-pipelines.yml +0 -0
  150. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/workflows/buildkite.yml +0 -0
  151. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/workflows/github-actions.yml +0 -0
  152. {socketsecurity-2.4.20 → socketsecurity-2.5.0}/workflows/gitlab-ci.yml +0 -0
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Added: `--base-scan-id` / `--base-commit-sha` diff baseline overrides
6
+
7
+ - New mutually exclusive flags to control which full scan a diff is compared
8
+ against, instead of always using the repository's latest head scan:
9
+ - `--base-scan-id <id>` diffs against that full scan ID verbatim.
10
+ - `--base-commit-sha <sha>` diffs against the most recent full scan created
11
+ from that commit — e.g. the PR's merge base from
12
+ `git merge-base origin/main HEAD` — so PR diffs are not polluted by
13
+ default-branch commits the PR never branched from.
14
+ - A `--base-commit-sha` with no matching full scan is a hard error (exit code 3,
15
+ or `--exit-code-on-api-error`; exit 0 with `--disable-blocking`) rather than a
16
+ silent fallback to the head scan, since diffing against the wrong baseline
17
+ misreports which alerts a PR introduces.
18
+ - Both flags are also settable via `--config` files (`base_scan_id`,
19
+ `base_commit_sha`).
20
+ - **Requirement:** `--base-commit-sha` looks up an existing scan — it does not
21
+ create one. Using it requires CI to run `socketcli` on every commit that lands
22
+ on the default branch; see the "Diffing against the merge base" note in
23
+ `docs/cli-reference.md` for the failure modes and a backfill pattern.
24
+
3
25
  ## 2.4.20
4
26
 
5
27
  ### Changed: bump pinned @coana-tech/cli to 15.8.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.4.20
3
+ Version: 2.5.0
4
4
  Summary: Socket Security CLI for CI/CD
5
5
  Project-URL: Homepage, https://socket.dev
6
6
  Author-email: Douglas Coburn <douglas@socket.dev>
@@ -102,6 +102,26 @@ socketcli --target-path .
102
102
  socketcli --enable-gitlab-security --gitlab-security-file gl-dependency-scanning-report.json
103
103
  ```
104
104
 
105
+ ### PR scan diffed against the merge base
106
+
107
+ By default, PR scans are diffed against the repository's latest head scan. To diff against
108
+ the exact commit your PR branched from instead, pass the merge base as the baseline:
109
+
110
+ ```bash
111
+ BASE_SHA=$(git merge-base origin/main HEAD)
112
+ socketcli --pr-number 123 --base-commit-sha "$BASE_SHA"
113
+ ```
114
+
115
+ > **Requirement:** `--base-commit-sha` only works if Socket already has a full scan for that
116
+ > exact commit. In practice this means your CI must run `socketcli` on **every commit that
117
+ > lands on your default branch** — not just some of them. If merges can land without a scan
118
+ > (skipped/canceled builds, `[skip ci]`, path-filtered pipelines), the PR scan will fail with
119
+ > exit code 3 rather than silently diff against the wrong baseline. See
120
+ > [`docs/cli-reference.md`](https://github.com/SocketDev/socket-python-cli/blob/main/docs/cli-reference.md)
121
+ > for the full requirements and a backfill pattern that makes PR jobs self-sufficient.
122
+
123
+ A specific full scan ID also works: `--base-scan-id <id>`.
124
+
105
125
  ## SARIF use cases
106
126
 
107
127
  ### Full-scope reachable SARIF (grouped alerts)
@@ -264,8 +284,10 @@ Minimal pattern:
264
284
  | `3` | Infrastructure or API error (timeout, network failure, unexpected error) |
265
285
 
266
286
  `--exit-code-on-api-error <N>` remaps the infrastructure-error code (`3`) to any
267
- value — e.g. a Buildkite `soft_fail` code, or `0` to swallow infra errors. Exit
268
- `3` is a Socket convention, not an industry standard.
287
+ value — e.g. a Buildkite
288
+ [`soft_fail`](https://buildkite.com/docs/pipelines/configure/step-types/command-step)
289
+ code, or `0` to swallow infra errors. Exit `3` is a Socket convention, not an
290
+ industry standard.
269
291
 
270
292
  ### How these options interact
271
293
 
@@ -42,6 +42,26 @@ socketcli --target-path .
42
42
  socketcli --enable-gitlab-security --gitlab-security-file gl-dependency-scanning-report.json
43
43
  ```
44
44
 
45
+ ### PR scan diffed against the merge base
46
+
47
+ By default, PR scans are diffed against the repository's latest head scan. To diff against
48
+ the exact commit your PR branched from instead, pass the merge base as the baseline:
49
+
50
+ ```bash
51
+ BASE_SHA=$(git merge-base origin/main HEAD)
52
+ socketcli --pr-number 123 --base-commit-sha "$BASE_SHA"
53
+ ```
54
+
55
+ > **Requirement:** `--base-commit-sha` only works if Socket already has a full scan for that
56
+ > exact commit. In practice this means your CI must run `socketcli` on **every commit that
57
+ > lands on your default branch** — not just some of them. If merges can land without a scan
58
+ > (skipped/canceled builds, `[skip ci]`, path-filtered pipelines), the PR scan will fail with
59
+ > exit code 3 rather than silently diff against the wrong baseline. See
60
+ > [`docs/cli-reference.md`](https://github.com/SocketDev/socket-python-cli/blob/main/docs/cli-reference.md)
61
+ > for the full requirements and a backfill pattern that makes PR jobs self-sufficient.
62
+
63
+ A specific full scan ID also works: `--base-scan-id <id>`.
64
+
45
65
  ## SARIF use cases
46
66
 
47
67
  ### Full-scope reachable SARIF (grouped alerts)
@@ -204,8 +224,10 @@ Minimal pattern:
204
224
  | `3` | Infrastructure or API error (timeout, network failure, unexpected error) |
205
225
 
206
226
  `--exit-code-on-api-error <N>` remaps the infrastructure-error code (`3`) to any
207
- value — e.g. a Buildkite `soft_fail` code, or `0` to swallow infra errors. Exit
208
- `3` is a Socket convention, not an industry standard.
227
+ value — e.g. a Buildkite
228
+ [`soft_fail`](https://buildkite.com/docs/pipelines/configure/step-types/command-step)
229
+ code, or `0` to swallow infra errors. Exit `3` is a Socket convention, not an
230
+ industry standard.
209
231
 
210
232
  ### How these options interact
211
233
 
@@ -0,0 +1,167 @@
1
+ # CI/CD guide
2
+
3
+ Use this guide for pipeline-focused CLI usage across platforms.
4
+
5
+ ## Recommended patterns
6
+
7
+ ### Dashboard-style reachable SARIF
8
+
9
+ ```bash
10
+ socketcli \
11
+ --reach \
12
+ --sarif-file results.sarif \
13
+ --sarif-scope full \
14
+ --sarif-grouping alert \
15
+ --sarif-reachability reachable \
16
+ --disable-blocking
17
+ ```
18
+
19
+ ### Diff-based gating on new reachable findings
20
+
21
+ ```bash
22
+ socketcli \
23
+ --reach \
24
+ --sarif-file results.sarif \
25
+ --sarif-scope diff \
26
+ --sarif-reachability reachable \
27
+ --strict-blocking
28
+ ```
29
+
30
+ ## Config file usage in CI
31
+
32
+ Use `--config .socketcli.toml` or `--config .socketcli.json` to keep pipeline commands small.
33
+
34
+ Precedence order:
35
+
36
+ `CLI flags` > `environment variables` > `config file` > `built-in defaults`
37
+
38
+ Example:
39
+
40
+ ```toml
41
+ [socketcli]
42
+ reach = true
43
+ sarif_scope = "full"
44
+ sarif_grouping = "alert"
45
+ sarif_reachability = "reachable"
46
+ sarif_file = "results.sarif"
47
+ ```
48
+
49
+ Equivalent JSON:
50
+
51
+ ```json
52
+ {
53
+ "socketcli": {
54
+ "reach": true,
55
+ "sarif_scope": "full",
56
+ "sarif_grouping": "alert",
57
+ "sarif_reachability": "reachable",
58
+ "sarif_file": "results.sarif"
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Platform examples
64
+
65
+ ### GitHub Actions
66
+
67
+ ```yaml
68
+ - name: Run Socket CLI
69
+ run: socketcli --config .socketcli.toml --target-path .
70
+ env:
71
+ SOCKET_SECURITY_API_TOKEN: ${{ secrets.SOCKET_SECURITY_API_TOKEN }}
72
+ ```
73
+
74
+ ### Buildkite
75
+
76
+ ```yaml
77
+ steps:
78
+ - label: "Socket scan"
79
+ command: "socketcli --config .socketcli.toml --target-path ."
80
+ env:
81
+ SOCKET_SECURITY_API_TOKEN: "${SOCKET_SECURITY_API_TOKEN}"
82
+ ```
83
+
84
+ #### Merge-base baselines in Buildkite (dynamic pipelines)
85
+
86
+ Notes for using `--base-commit-sha` (see the
87
+ [merge-base note in the CLI reference](cli-reference.md#pull-request-and-commit))
88
+ when your steps are emitted by a
89
+ [dynamic pipeline](https://buildkite.com/docs/pipelines/configure/dynamic-pipelines)
90
+ generator rather than a static YAML file:
91
+
92
+ - **Compute the merge base at generation time, not step time.** The generator runs
93
+ with a full checkout; step agents may have shallow or fresh clones where
94
+ `git merge-base` fails or needs an extra fetch. Resolve it once in the generator and
95
+ bake it into the emitted step's `env`. Diff against the PR's *target* branch, which
96
+ isn't always the default branch (see Buildkite's
97
+ [environment variables](https://buildkite.com/docs/pipelines/configure/environment-variables)):
98
+
99
+ ```shell
100
+ TARGET="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-$BUILDKITE_PIPELINE_DEFAULT_BRANCH}"
101
+ BASE_SHA=$(git merge-base "origin/${TARGET}" HEAD)
102
+ ```
103
+
104
+ - **Emit the backfill step conditionally from the generator.** The generator is the
105
+ natural place for the "does a baseline scan exist?" check
106
+ (`GET /orgs/{org}/full-scans?repo=<repo>&commit_hash=$BASE_SHA&per_page=1`): only
107
+ emit the baseline-scan step when it returns nothing. The emitted pipeline then shows
108
+ in the UI whether a backfill will run.
109
+
110
+ - **Keep the backfill inside one command step.** The checkout-base → scan →
111
+ checkout-PR sequence must not be split across steps — steps can land on different
112
+ agents with different checkouts. Prefer
113
+ [`git worktree`](https://git-scm.com/docs/git-worktree) over mutating the step's
114
+ checkout: `git worktree add /tmp/socket-base "$BASE_SHA"` then
115
+ `socketcli --target-path /tmp/socket-base --branch "$TARGET" --disable-blocking`.
116
+
117
+ - **Soft-fail infra errors, not findings.** A missing baseline (or any API error)
118
+ exits with code 3 (`--exit-code-on-api-error` to change it); real findings exit 1.
119
+ [`soft_fail: [{exit_status: 3}]`](https://buildkite.com/docs/pipelines/configure/step-types/command-step)
120
+ on the PR scan step keeps infra errors from blocking merges while security findings
121
+ still do.
122
+
123
+ - **["Cancel intermediate builds"](https://buildkite.com/docs/pipelines/configure/canceling-builds#cancel-running-intermediate-builds)
124
+ on the default branch is the main source of baseline gaps.** Canceled builds never
125
+ scan their commit, so merge-base lookups for PRs based on those commits fail. The
126
+ conditional backfill step above is the remedy; there is no per-step exemption from
127
+ build cancellation in Buildkite. If you need strict scan-once semantics for
128
+ concurrent backfills of the same merge base, serialize the backfill step with a
129
+ [concurrency group](https://buildkite.com/docs/pipelines/configure/workflows/controlling-concurrency)
130
+ keyed on the merge-base SHA.
131
+
132
+ ### GitLab CI
133
+
134
+ ```yaml
135
+ socket_scan:
136
+ script:
137
+ - socketcli --config .socketcli.toml --target-path .
138
+ variables:
139
+ SOCKET_SECURITY_API_TOKEN: $SOCKET_SECURITY_API_TOKEN
140
+ ```
141
+
142
+ ### Bitbucket Pipelines
143
+
144
+ ```yaml
145
+ pipelines:
146
+ default:
147
+ - step:
148
+ script:
149
+ - socketcli --config .socketcli.toml --target-path .
150
+ ```
151
+
152
+ ## Workflow templates
153
+
154
+ Prebuilt examples in this repo:
155
+
156
+ - [`../workflows/github-actions.yml`](../workflows/github-actions.yml)
157
+ - [`../workflows/buildkite.yml`](../workflows/buildkite.yml)
158
+ - [`../workflows/gitlab-ci.yml`](../workflows/gitlab-ci.yml)
159
+ - [`../workflows/bitbucket-pipelines.yml`](../workflows/bitbucket-pipelines.yml)
160
+
161
+ ## CI gotchas
162
+
163
+ - `--strict-blocking` enables strict diff behavior (`new + unchanged`) for blocking evaluation and diff-based output selection.
164
+ - `--sarif-scope full` requires `--reach`.
165
+ - `--sarif-grouping alert` currently applies to `--sarif-scope full`.
166
+ - Diff-based SARIF can validly be empty when there are no matching net-new alerts.
167
+ - Keep API tokens in secret stores (`SOCKET_SECURITY_API_TOKEN`), not in config files.
@@ -146,6 +146,7 @@ This will simultaneously generate:
146
146
  socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--repo-is-public] [--branch BRANCH] [--integration {api,github,gitlab,azure,bitbucket}]
147
147
  [--config <path>]
148
148
  [--owner OWNER] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA] [--committers [COMMITTERS ...]]
149
+ [--base-scan-id BASE_SCAN_ID | --base-commit-sha BASE_COMMIT_SHA]
149
150
  [--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
150
151
  [--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
151
152
  [--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--exclude-paths EXCLUDE_PATHS] [--include-dirs INCLUDE_DIRS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
@@ -191,6 +192,39 @@ If you don't want to provide the Socket API Token every time then you can use th
191
192
  | `--pr-number` | False | "0" | Pull request number |
192
193
  | `--commit-message` | False | *auto* | Commit message (auto-detected from git) |
193
194
  | `--commit-sha` | False | *auto* | Commit SHA (auto-detected from git) |
195
+ | `--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` |
196
+ | `--base-commit-sha`| False | | Commit SHA to diff against, overriding the repository's head scan as the baseline. The most recent full scan for that commit is used; the CLI errors (exit code 3, or `--exit-code-on-api-error`) if no scan exists for it. Mutually exclusive with `--base-scan-id` |
197
+
198
+ > **Diffing against the merge base** — by default, PR scans are diffed against the repository's *latest* head scan, which may include newer default-branch commits than your PR branched from. To diff against the exact commit your PR is based on, compute the merge base and pass it as the baseline:
199
+ >
200
+ > ```shell
201
+ > BASE_SHA=$(git merge-base origin/main HEAD)
202
+ > socketcli --pr-number 123 --base-commit-sha "$BASE_SHA"
203
+ > ```
204
+ >
205
+ > **Requirement: a full scan must already exist for the merge-base commit.** `--base-commit-sha` does not create a scan of that commit; it looks up an existing one. That lookup only succeeds if your CI runs `socketcli` on **every commit that lands on your default branch** — every merge and direct push, not just periodic or latest-only scans. Common ways commits slip through without a scan:
206
+ >
207
+ > - CI settings that cancel or skip intermediate builds when newer commits land (e.g. Buildkite's ["cancel intermediate builds"](https://buildkite.com/docs/pipelines/configure/canceling-builds#cancel-running-intermediate-builds))
208
+ > - `[skip ci]` commits, path-filtered pipelines, or failed/canceled scan steps
209
+ > - merge-base commits that predate your Socket rollout
210
+ >
211
+ > If no scan exists for the commit, 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 head scan — a wrong baseline would misreport which alerts the PR introduces. Don't adopt this flag without default-branch scan coverage in place; you'll fail PR builds on lookup misses.
212
+ >
213
+ > **Backfill pattern** — if your default-branch coverage has gaps, the PR job can create the missing baseline itself before scanning:
214
+ >
215
+ > ```shell
216
+ > BASE_SHA=$(git merge-base origin/main HEAD)
217
+ > # Create the baseline only if Socket doesn't have one for this commit yet
218
+ > # (check: GET /orgs/{org}/full-scans?repo=<repo>&commit_hash=$BASE_SHA&per_page=1)
219
+ > git checkout "$BASE_SHA"
220
+ > socketcli --branch main --disable-blocking
221
+ > git checkout -
222
+ > socketcli --pr-number 123 --base-commit-sha "$BASE_SHA"
223
+ > ```
224
+ >
225
+ > Run the baseline step with `--disable-blocking` (findings on the default branch must not fail the PR job) and an explicit `--branch`, since branch auto-detection is unreliable at a detached HEAD.
226
+ >
227
+ > Buildkite users with dynamically generated pipelines: see [Merge-base baselines in Buildkite](ci-cd.md#merge-base-baselines-in-buildkite-dynamic-pipelines) for generation-time vs. step-time guidance.
194
228
 
195
229
  #### Path and File
196
230
  | Parameter | Required | Default | Description |
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.4.20"
9
+ version = "2.5.0"
10
10
  requires-python = ">= 3.11"
11
11
  license = {"file" = "LICENSE"}
12
12
  dependencies = [
@@ -1,3 +1,3 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.4.20'
2
+ __version__ = '2.5.0'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -119,6 +119,8 @@ class CliConfig:
119
119
  scm: str = "api"
120
120
  sbom_file: Optional[str] = None
121
121
  commit_sha: str = ""
122
+ base_scan_id: Optional[str] = None
123
+ base_commit_sha: Optional[str] = None
122
124
  generate_license: bool = False
123
125
  enable_debug: bool = False
124
126
  allow_unverified: bool = False
@@ -265,6 +267,8 @@ class CliConfig:
265
267
  'scm': args.scm,
266
268
  'sbom_file': args.sbom_file,
267
269
  'commit_sha': args.commit_sha,
270
+ 'base_scan_id': args.base_scan_id,
271
+ 'base_commit_sha': args.base_commit_sha,
268
272
  'generate_license': args.generate_license,
269
273
  'enable_debug': args.enable_debug,
270
274
  'enable_diff': args.enable_diff,
@@ -406,6 +410,12 @@ class CliConfig:
406
410
  logging.error("--workspace-name requires --sub-path to be specified")
407
411
  exit(1)
408
412
 
413
+ # argparse only enforces the mutually exclusive group for real CLI args;
414
+ # this also catches both values arriving via a --config file.
415
+ if args.base_scan_id and args.base_commit_sha:
416
+ logging.error("--base-scan-id and --base-commit-sha are mutually exclusive")
417
+ exit(1)
418
+
409
419
  if args.sarif_scope == "full" and not args.reach:
410
420
  logging.error("--sarif-scope full requires --reach to be specified")
411
421
  exit(1)
@@ -560,6 +570,25 @@ def create_argument_parser() -> argparse.ArgumentParser:
560
570
  help="Committer for the commit (comma separated)",
561
571
  nargs="*"
562
572
  )
573
+ base_scan_group = pr_group.add_mutually_exclusive_group()
574
+ base_scan_group.add_argument(
575
+ "--base-scan-id",
576
+ dest="base_scan_id",
577
+ metavar="<id>",
578
+ default=None,
579
+ help="Full scan ID to diff the new scan against, overriding the repository's "
580
+ "head scan as the baseline. Mutually exclusive with --base-commit-sha."
581
+ )
582
+ base_scan_group.add_argument(
583
+ "--base-commit-sha",
584
+ dest="base_commit_sha",
585
+ metavar="<sha>",
586
+ default=None,
587
+ help="Commit SHA to diff the new scan against, overriding the repository's head "
588
+ "scan as the baseline. The most recent full scan matching this commit (e.g. "
589
+ "the merge base from 'git merge-base origin/main HEAD') is used; the CLI "
590
+ "errors if no scan exists for it. Mutually exclusive with --base-scan-id."
591
+ )
563
592
 
564
593
  # Path and File options
565
594
  path_group = parser.add_argument_group('Path and File')
@@ -1154,6 +1154,94 @@ class Core:
1154
1154
  repo_info = self.get_repo_info(repo_slug)
1155
1155
  return repo_info.head_full_scan_id if repo_info.head_full_scan_id else None
1156
1156
 
1157
+ def get_full_scan_id_by_commit(
1158
+ self,
1159
+ repo_slug: str,
1160
+ commit_sha: str,
1161
+ workspace: Optional[str] = None,
1162
+ scan_type: Optional[str] = None
1163
+ ) -> Optional[str]:
1164
+ """
1165
+ Finds the most recent full scan for a repository + commit SHA.
1166
+
1167
+ Used by --base-commit-sha to resolve the diff baseline (e.g. the merge-base
1168
+ commit of a PR). When the same commit was scanned more than once, the newest
1169
+ scan wins.
1170
+
1171
+ Args:
1172
+ repo_slug: Repository slug the scan belongs to
1173
+ commit_sha: Commit SHA the scan was created from
1174
+ workspace: Socket workspace the scan belongs to, if any
1175
+ scan_type: Socket scan type to match, if any
1176
+
1177
+ Returns:
1178
+ Full scan ID if one exists for that commit, None otherwise
1179
+ """
1180
+ query_params = {
1181
+ "repo": repo_slug,
1182
+ "commit_hash": commit_sha,
1183
+ "sort": "created_at",
1184
+ "direction": "desc",
1185
+ "per_page": 1,
1186
+ }
1187
+ if workspace:
1188
+ query_params["workspace"] = workspace
1189
+ if scan_type:
1190
+ query_params["scan_type"] = scan_type
1191
+
1192
+ response = self.sdk.fullscans.get(
1193
+ self.config.org_slug,
1194
+ query_params,
1195
+ )
1196
+ results = response.get("results") if isinstance(response, dict) else None
1197
+ if not results:
1198
+ return None
1199
+ return results[0].get("id")
1200
+
1201
+ def resolve_base_full_scan_id(self, params: FullScanParams) -> Optional[str]:
1202
+ """
1203
+ Resolves the baseline full scan ID to diff a new scan against.
1204
+
1205
+ Priority: --base-scan-id (used verbatim), then --base-commit-sha (newest
1206
+ full scan for that commit), then the repository's current head scan. A
1207
+ --base-commit-sha with no matching full scan is a hard error rather than a
1208
+ silent fallback to the head scan, because diffing against the wrong
1209
+ baseline silently misreports which alerts a PR introduces.
1210
+
1211
+ Returns:
1212
+ Full scan ID to use as the diff baseline, or None when the repository
1213
+ has no head scan yet (caller creates an empty baseline scan).
1214
+ """
1215
+ if self.cli_config and self.cli_config.base_scan_id:
1216
+ log.info(f"Using full scan {self.cli_config.base_scan_id} as diff baseline (--base-scan-id)")
1217
+ return self.cli_config.base_scan_id
1218
+
1219
+ if self.cli_config and self.cli_config.base_commit_sha:
1220
+ commit_sha = self.cli_config.base_commit_sha
1221
+ scan_id = self.get_full_scan_id_by_commit(
1222
+ params.repo,
1223
+ commit_sha,
1224
+ workspace=params.workspace,
1225
+ scan_type=params.scan_type,
1226
+ )
1227
+ if scan_id is None:
1228
+ log.error(
1229
+ f"No full scan found for commit {commit_sha} in repo {params.repo} "
1230
+ "(--base-commit-sha). Ensure a scan was created for that commit "
1231
+ "(e.g. the CLI runs on default-branch pushes), or pass "
1232
+ "--base-scan-id instead."
1233
+ )
1234
+ if self.cli_config.disable_blocking:
1235
+ sys.exit(0)
1236
+ sys.exit(self.cli_config.exit_code_on_api_error)
1237
+ log.info(f"Using full scan {scan_id} (commit {commit_sha}) as diff baseline (--base-commit-sha)")
1238
+ return scan_id
1239
+
1240
+ try:
1241
+ return self.get_head_scan_for_repo(params.repo)
1242
+ except APIResourceNotFound:
1243
+ return None
1244
+
1157
1245
  @staticmethod
1158
1246
  def update_package_values(pkg: Package) -> Package:
1159
1247
  pkg.purl = f"{pkg.name}@{pkg.version}"
@@ -1242,8 +1330,8 @@ class Core:
1242
1330
  OVERWRITES whatever the diff embedded, before anything reads it.
1243
1331
  Either way the embedded license payload is dead weight, and on
1244
1332
  large dependency trees it inflated the diff response past ~2.3MB
1245
- and truncated it mid-string, crashing ``response.json()``
1246
- (CE-224, customer: Tremendous). Defaulting to ``False`` keeps the
1333
+ and truncated it mid-string, crashing ``response.json()``.
1334
+ Defaulting to ``False`` keeps the
1247
1335
  diff lean with zero change to any output artifact. The parameter
1248
1336
  is retained as an explicit override seam, not wired to the
1249
1337
  ``--exclude-license-details`` user flag (which still governs the
@@ -1388,11 +1476,9 @@ class Core:
1388
1476
  log.info("No supported manifest files found - creating empty scan for diff comparison")
1389
1477
  scan_files = Core.empty_head_scan_file()
1390
1478
 
1391
- try:
1392
- # Get head scan ID
1393
- head_full_scan_id = self.get_head_scan_for_repo(params.repo)
1394
- except APIResourceNotFound:
1395
- head_full_scan_id = None
1479
+ # Resolve the baseline scan: --base-scan-id / --base-commit-sha override
1480
+ # the repository's head scan (None only when the repo has no head scan yet).
1481
+ head_full_scan_id = self.resolve_base_full_scan_id(params)
1396
1482
 
1397
1483
  # If no head scan exists, create an empty baseline scan
1398
1484
  if head_full_scan_id is None:
@@ -1475,7 +1561,7 @@ class Core:
1475
1561
  # (the --exclude-license-details user flag) into the diff request. The
1476
1562
  # diff path never consumes embedded license data (see
1477
1563
  # get_added_and_removed_packages docstring), so requesting it only bloats
1478
- # the response and risks the CE-224 truncation crash on large repos. The
1564
+ # the response and risks the truncation crash on large repos. The
1479
1565
  # user flag still controls the dashboard report URL below; it just no
1480
1566
  # longer gates this internal diff payload.
1481
1567
  (