socketsecurity 2.7.2__tar.gz → 2.8.1__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 (165) hide show
  1. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/actions/setup-docker/action.yml +2 -2
  2. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/version-check.yml +50 -9
  3. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/CHANGELOG.md +44 -0
  4. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/PKG-INFO +9 -5
  5. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/README.md +4 -0
  6. socketsecurity-2.8.1/docs/ci-cd.md +413 -0
  7. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/docs/cli-reference.md +70 -7
  8. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/pyproject.toml +5 -5
  9. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/__init__.py +1 -1
  10. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/__init__.py +67 -10
  11. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/tools/reachability.py +3 -3
  12. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/formatters/slack.py +6 -0
  13. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/socketcli.py +16 -0
  14. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_sdk_methods.py +32 -0
  15. socketsecurity-2.8.1/tests/unit/test_scan_scope.py +140 -0
  16. socketsecurity-2.8.1/tests/unit/test_slack_severity_normalization.py +76 -0
  17. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_socketcli.py +14 -0
  18. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/uv.lock +96 -85
  19. socketsecurity-2.7.2/docs/ci-cd.md +0 -180
  20. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/CODEOWNERS +0 -0
  21. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  22. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  23. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  24. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  25. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/actions/setup-hatch/action.yml +0 -0
  26. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/actions/setup-sfw/action.yml +0 -0
  27. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/dependabot.yml +0 -0
  28. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/dependency-review.yml +0 -0
  29. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/docker-stable.yml +0 -0
  30. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/e2e-test.yml +0 -0
  31. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/package-check.yml +0 -0
  32. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/pr-preview.yml +0 -0
  33. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/python-tests.yml +0 -0
  34. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/workflows/release.yml +0 -0
  35. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.github/zizmor.yml +0 -0
  36. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.gitignore +0 -0
  37. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.hooks/sync_version.py +0 -0
  38. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.pre-commit-config.yaml +0 -0
  39. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/.python-version +0 -0
  40. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/CONTRIBUTING.md +0 -0
  41. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/Dockerfile +0 -0
  42. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/Dockerfile.preview +0 -0
  43. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/LICENSE +0 -0
  44. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/Makefile +0 -0
  45. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/benchmarks/manifest_discovery.py +0 -0
  46. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/docs/development.md +0 -0
  47. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/docs/troubleshooting.md +0 -0
  48. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-dashboard-parity.json +0 -0
  49. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-dashboard-parity.toml +0 -0
  50. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-diff-ci-cd.json +0 -0
  51. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-diff-ci-cd.toml +0 -0
  52. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-instance-detail.json +0 -0
  53. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/examples/config/sarif-instance-detail.toml +0 -0
  54. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/instructions/gitlab-commit-status/uat.md +0 -0
  55. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/pytest.ini +0 -0
  56. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/build_container.sh +0 -0
  57. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/build_container_flexible.sh +0 -0
  58. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/deploy-test-docker.sh +0 -0
  59. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/deploy-test-pypi.sh +0 -0
  60. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/docker-entrypoint.sh +0 -0
  61. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/scripts/run.sh +0 -0
  62. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/session.md +0 -0
  63. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socket.yml +0 -0
  64. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/config.py +0 -0
  65. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/alert_selection.py +0 -0
  66. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/classes.py +0 -0
  67. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/cli_client.py +0 -0
  68. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/cli_run.py +0 -0
  69. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/exceptions.py +0 -0
  70. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/git_interface.py +0 -0
  71. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/helper/__init__.py +0 -0
  72. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  73. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/lazy_file_loader.py +0 -0
  74. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/log_uploader.py +0 -0
  75. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/logging.py +0 -0
  76. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/messages.py +0 -0
  77. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/resource_utils.py +0 -0
  78. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm/__init__.py +0 -0
  79. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm/base.py +0 -0
  80. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm/client.py +0 -0
  81. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm/github.py +0 -0
  82. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm/gitlab.py +0 -0
  83. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/scm_comments.py +0 -0
  84. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/socket_config.py +0 -0
  85. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/streaming.py +0 -0
  86. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/core/utils.py +0 -0
  87. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/fossa_compat.py +0 -0
  88. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/output.py +0 -0
  89. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/__init__.py +0 -0
  90. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/base.py +0 -0
  91. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/formatters/__init__.py +0 -0
  92. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/jira.py +0 -0
  93. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/manager.py +0 -0
  94. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/slack.py +0 -0
  95. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/teams.py +0 -0
  96. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/socketsecurity/plugins/webhook.py +0 -0
  97. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/__init__.py +0 -0
  98. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/conftest.py +0 -0
  99. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/create_diff_input.json +0 -0
  100. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_diff_alerts.py +0 -0
  101. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_diff_generation.py +0 -0
  102. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_diff_scan_polling.py +0 -0
  103. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_facts_compression.py +0 -0
  104. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_has_manifest_files.py +0 -0
  105. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_invalid_facts_marker.py +0 -0
  106. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_package_and_alerts.py +0 -0
  107. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/core/test_supporting_methods.py +0 -0
  108. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/create_response.json +0 -0
  109. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/diff/stream_diff.json +0 -0
  110. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  111. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/head_scan/metadata.json +0 -0
  112. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  113. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  114. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/new_scan/metadata.json +0 -0
  115. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  116. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/repos/repo_info_error.json +0 -0
  117. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/repos/repo_info_no_head.json +0 -0
  118. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/repos/repo_info_success.json +0 -0
  119. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/data/settings/security-policy.json +0 -0
  120. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  121. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  122. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  123. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/reach-facts-probe.sh +0 -0
  124. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/validate-gitlab.sh +0 -0
  125. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/validate-json.sh +0 -0
  126. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/validate-reachability.sh +0 -0
  127. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/validate-sarif.sh +0 -0
  128. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/e2e/validate-scan.sh +0 -0
  129. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/fixtures/fossa/README.md +0 -0
  130. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  131. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  132. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  133. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  134. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/__init__.py +0 -0
  135. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_alert_selection.py +0 -0
  136. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_cli_config.py +0 -0
  137. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_cli_run.py +0 -0
  138. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_client.py +0 -0
  139. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_config.py +0 -0
  140. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_dependency_overview.py +0 -0
  141. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_disable_ignore.py +0 -0
  142. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_exclude_paths.py +0 -0
  143. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_fossa_compat.py +0 -0
  144. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_fossa_parity.py +0 -0
  145. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_full_scan_retry.py +0 -0
  146. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_git_interface.py +0 -0
  147. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_github_buildkite_config.py +0 -0
  148. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_gitlab_auth.py +0 -0
  149. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  150. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_gitlab_commit_status.py +0 -0
  151. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_gitlab_format.py +0 -0
  152. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  153. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_include_dirs.py +0 -0
  154. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_log_uploader.py +0 -0
  155. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_manifest_discovery.py +0 -0
  156. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_output.py +0 -0
  157. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_pr_comment_rendering.py +0 -0
  158. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_reachability.py +0 -0
  159. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_slack_plugin.py +0 -0
  160. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_streaming.py +0 -0
  161. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/tests/unit/test_tier1_finalize.py +0 -0
  162. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/workflows/bitbucket-pipelines.yml +0 -0
  163. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/workflows/buildkite.yml +0 -0
  164. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/workflows/github-actions.yml +0 -0
  165. {socketsecurity-2.7.2 → socketsecurity-2.8.1}/workflows/gitlab-ci.yml +0 -0
@@ -19,9 +19,9 @@ inputs:
19
19
  runs:
20
20
  using: "composite"
21
21
  steps:
22
- - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
22
+ - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0
23
23
  if: inputs.enable-qemu == 'true'
24
- - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
24
+ - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
25
25
  - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
26
26
  with:
27
27
  username: ${{ inputs.dockerhub-username }}
@@ -6,6 +6,8 @@ on:
6
6
  - 'socketsecurity/**'
7
7
  - 'pyproject.toml'
8
8
  - 'uv.lock'
9
+ # Included so a change to the check itself is exercised by its own PR.
10
+ - '.github/workflows/version-check.yml'
9
11
 
10
12
  permissions:
11
13
  contents: read
@@ -42,16 +44,37 @@ jobs:
42
44
  export PR_VERSION
43
45
  export MAIN_VERSION
44
46
 
45
- # Compare against both main and latest published PyPI release.
47
+ # Compare against the latest published PyPI release.
46
48
  python3 <<'PY'
47
49
  import json
48
50
  import os
51
+ import tomllib
49
52
  import urllib.request
50
53
  from packaging import version
51
54
 
52
55
  pr_ver = version.parse(os.environ["PR_VERSION"])
53
56
  main_ver = version.parse(os.environ["MAIN_VERSION"])
54
57
 
58
+ with open("pyproject.toml", "rb") as fh:
59
+ pyproject_ver = version.parse(tomllib.load(fh)["project"]["version"])
60
+
61
+ # The version is two hand-maintained literals with nothing deriving one
62
+ # from the other: pyproject.toml is what actually gets published, and
63
+ # socketsecurity/__init__.py is what the CLI reports as its User-Agent.
64
+ # Every comparison below reads only __init__.py, so bumping that alone
65
+ # would pass this job and then publish under the old number -- caught
66
+ # late, by twine rejecting an existing file, after the merge. Require
67
+ # the two to agree before comparing anything. (uv.lock carries a third
68
+ # copy, but uv derives it and `uv lock --locked` in python-tests
69
+ # already fails when it drifts.)
70
+ if pr_ver != pyproject_ver:
71
+ print(
72
+ f"❌ Version mismatch inside the PR: pyproject.toml is "
73
+ f"{pyproject_ver}, socketsecurity/__init__.py is {pr_ver}. "
74
+ f"Bump both."
75
+ )
76
+ raise SystemExit(1)
77
+
55
78
  with urllib.request.urlopen("https://pypi.org/pypi/socketsecurity/json") as response:
56
79
  pypi_data = json.load(response)
57
80
 
@@ -62,19 +85,37 @@ jobs:
62
85
  published_versions.append(parsed)
63
86
 
64
87
  pypi_ver = max(published_versions) if published_versions else version.parse("0.0.0")
65
- required_floor = max(main_ver, pypi_ver)
66
88
 
67
- if pr_ver <= required_floor:
89
+ # The only hard requirement is that the version is ahead of what is
90
+ # actually released. Treating main's version as a second floor breaks
91
+ # the legitimate case where several PRs share one unreleased release:
92
+ # the first bumps main to the new version and the rest ride it without
93
+ # bumping again, which is what keeps them under a single changelog
94
+ # header. Main is therefore only a floor when this PR moves the
95
+ # version -- a change to it must go forwards, never backwards.
96
+ if pr_ver <= pypi_ver:
68
97
  print(
69
- f"❌ Version must be greater than main and PyPI! "
70
- f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}"
98
+ f"❌ Version {pr_ver} is already published on PyPI "
99
+ f"(latest release: {pypi_ver}). Bump it."
100
+ )
101
+ raise SystemExit(1)
102
+
103
+ if pr_ver < main_ver:
104
+ print(
105
+ f"❌ Version moves backwards: main is {main_ver}, PR is {pr_ver}."
71
106
  )
72
107
  raise SystemExit(1)
73
108
 
74
- print(
75
- f"✅ Version properly incremented. "
76
- f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}"
77
- )
109
+ if pr_ver == main_ver:
110
+ print(
111
+ f" Riding main's unreleased {pr_ver} "
112
+ f"(latest PyPI release: {pypi_ver})."
113
+ )
114
+ else:
115
+ print(
116
+ f"✅ Version properly incremented. "
117
+ f"Main: {main_ver}, PyPI: {pypi_ver}, PR: {pr_ver}"
118
+ )
78
119
  PY
79
120
 
80
121
  - name: Require uv.lock update when pyproject changes
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.8.1
4
+
5
+ ### Changed: bump pinned @coana-tech/cli to 15.10.40
6
+
7
+ - Bumped the pinned reachability engine (`@coana-tech/cli`) from `15.10.39` to
8
+ `15.10.40`. See the
9
+ [reachability analysis changelog](https://docs.socket.dev/docs/reachability-analysis-changelog)
10
+ for engine changes.
11
+
12
+ ## 2.8.0
13
+
14
+ ### Changed: improve monorepo scan diagnostics and guidance
15
+
16
+ - Added aggregate scan configuration, manifest-count, baseline-selection, and
17
+ fallback diagnostics without listing submitted manifest paths.
18
+ - Clarified monorepo scan scoping, workspace flags, CI path filters, and timeout
19
+ behavior, with a changed-workspace GitHub Actions example.
20
+
21
+ ### Changed: bump socketdev to 3.6.0
22
+
23
+ - Bumped the pinned SDK (`socketdev`) from `3.5.0` to `3.6.0`. Its package-type
24
+ enum gained ten members — `alpm`, `chrome`, `clawhub`, `edge-extension`,
25
+ `firefox-extension`, `qpkg`, `socket`, `swid`, `vscode` and
26
+ `vscode-extension` — so artifacts of those types are now reported under their
27
+ own type instead of falling back to `unknown`.
28
+
29
+ ### Fixed: apply configured exit codes to API failures
30
+
31
+ - Full-scan and streamed-diff API failures now use the configured infrastructure
32
+ error exit code instead of the security-finding exit code.
33
+
34
+ ### Fixed: mid-severity findings were dropped from the Slack summary
35
+
36
+ - The Slack reachability formatter keyed every severity lookup on `medium`,
37
+ but the API sends `middle`. A mid-severity finding therefore missed all of
38
+ them at once: it was not counted, so the summary always read `Medium: 0`; it
39
+ was excluded from `total_findings`, which can drive the "and N more" count
40
+ negative; and it sorted at the default order of 4, below `low`, so it was the
41
+ first thing truncated when the Slack block limit was reached.
42
+ - Severity is now normalized to one spelling when an alert is read, matching
43
+ how the GitLab and PR-comment paths already handle both forms. The findings
44
+ themselves were always listed; only the counts, ordering and truncation were
45
+ wrong.
46
+
3
47
  ## 2.7.2
4
48
 
5
49
  ### Changed: bump pinned @coana-tech/cli to 15.10.39
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: socketsecurity
3
- Version: 2.7.2
3
+ Version: 2.8.1
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>
@@ -35,7 +35,7 @@ Classifier: Programming Language :: Python :: 3.12
35
35
  Requires-Python: >=3.11
36
36
  Requires-Dist: beautifulsoup4==4.15.0
37
37
  Requires-Dist: brotli==1.2.0; platform_python_implementation == 'CPython'
38
- Requires-Dist: brotlicffi==1.2.0.1; platform_python_implementation != 'CPython'
38
+ Requires-Dist: brotlicffi==1.2.0.2; platform_python_implementation != 'CPython'
39
39
  Requires-Dist: gitpython==3.1.59
40
40
  Requires-Dist: markdown==3.10.3
41
41
  Requires-Dist: mdutils==1.8.1
@@ -43,13 +43,13 @@ Requires-Dist: packaging==26.3
43
43
  Requires-Dist: prettytable==3.18.0
44
44
  Requires-Dist: python-dotenv==1.2.3
45
45
  Requires-Dist: requests==2.34.2
46
- Requires-Dist: socketdev==3.5.0
46
+ Requires-Dist: socketdev==3.6.0
47
47
  Provides-Extra: dev
48
48
  Requires-Dist: hatch==1.18.0; extra == 'dev'
49
49
  Requires-Dist: pre-commit==4.6.2; extra == 'dev'
50
- Requires-Dist: ruff==0.16.4; extra == 'dev'
50
+ Requires-Dist: ruff==0.16.6; extra == 'dev'
51
51
  Requires-Dist: twine==7.0.0; extra == 'dev'
52
- Requires-Dist: uv==0.12.5; extra == 'dev'
52
+ Requires-Dist: uv==0.12.9; extra == 'dev'
53
53
  Provides-Extra: test
54
54
  Requires-Dist: pytest-asyncio==1.4.0; extra == 'test'
55
55
  Requires-Dist: pytest-cov==7.1.0; extra == 'test'
@@ -289,6 +289,10 @@ value — e.g. a Buildkite
289
289
  code, or `0` to swallow infra errors. Exit `3` is a Socket convention, not an
290
290
  industry standard.
291
291
 
292
+ This mapping applies to errors the CLI receives and handles. An external process
293
+ supervisor (for example GNU `timeout`) can terminate the CLI before it handles an
294
+ error, so the supervisor's exit status (commonly 124 or 137) takes precedence.
295
+
292
296
  ### How these options interact
293
297
 
294
298
  The two flags that affect exit codes can cancel each other out, so the order of
@@ -229,6 +229,10 @@ value — e.g. a Buildkite
229
229
  code, or `0` to swallow infra errors. Exit `3` is a Socket convention, not an
230
230
  industry standard.
231
231
 
232
+ This mapping applies to errors the CLI receives and handles. An external process
233
+ supervisor (for example GNU `timeout`) can terminate the CLI before it handles an
234
+ error, so the supervisor's exit status (commonly 124 or 137) takes precedence.
235
+
232
236
  ### How these options interact
233
237
 
234
238
  The two flags that affect exit codes can cancel each other out, so the order of
@@ -0,0 +1,413 @@
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
+ #### GitHub Actions: scan changed monorepo workspaces independently
75
+
76
+ GitHub Actions `paths` filters only decide whether a workflow starts. They do not
77
+ change `socketcli` discovery or upload scope. For a merge gate, it is usually safer
78
+ to start a small selector job on every PR update, then create one scan job per
79
+ affected logical workspace. This also avoids a required check remaining pending
80
+ when GitHub skips the entire workflow because of a top-level path filter.
81
+
82
+ This pattern produces one dashboard entry per logical workspace, which is what
83
+ gives each component its own alerts, baseline, and policy. It is also the layout
84
+ that grows the dashboard's repository list. See
85
+ [Choosing a scan layout](cli-reference.md#choosing-a-scan-layout) for when that
86
+ trade-off is worth making.
87
+
88
+ Define a repository variable named `SOCKET_MONOREPO_WORKSPACES_JSON`. Its value is
89
+ an array with one stable workspace name, one or more scan roots, and the path globs
90
+ that should select that workspace. Fill these placeholders with the repository's
91
+ real layout. A workspace definition selects directory roots; shared root manifests,
92
+ lockfiles, and cross-directory path dependencies outside those roots are not included
93
+ automatically.
94
+
95
+ ```json
96
+ [
97
+ {
98
+ "name": "<stable-workspace-name>",
99
+ "sub_paths": ["<repo-relative-scan-root>"],
100
+ "watch_globs": ["<repo-relative-changed-file-glob>"]
101
+ }
102
+ ]
103
+ ```
104
+
105
+ Each `sub_paths` value must be a directory, not an individual manifest or lockfile.
106
+ Using `.` includes the entire target path. Do not use this changed-workspace pattern
107
+ until the directory boundaries preserve every shared input needed to resolve each
108
+ logical graph. If root workspace metadata governs most or all of the repository, a
109
+ smaller coverage-preserving split may not be representable with `--sub-path` alone.
110
+
111
+ Also define `SOCKETCLI_VERSION` as the exact package version validated for the
112
+ workflow. The workflow below logs that version, uses full Git history for reliable
113
+ base/head selection, creates one matrix job (and therefore one graph and baseline)
114
+ per selected workspace, and fails closed on CLI/API/timeout failures. It uses API
115
+ SCM mode plus `--enable-diff` because parallel `--scm github` jobs can race while
116
+ updating the same PR comments; the matrix checks and report links are the gate.
117
+
118
+ ```yaml
119
+ name: Socket Security
120
+
121
+ on:
122
+ pull_request:
123
+ types: [opened, synchronize, reopened]
124
+ push:
125
+ branches: [main]
126
+
127
+ permissions:
128
+ contents: read
129
+
130
+ jobs:
131
+ select-workspaces:
132
+ runs-on: ubuntu-latest
133
+ outputs:
134
+ count: ${{ steps.select.outputs.count }}
135
+ matrix: ${{ steps.select.outputs.matrix }}
136
+ steps:
137
+ - uses: actions/checkout@v5
138
+ with:
139
+ fetch-depth: 0
140
+ persist-credentials: false
141
+
142
+ - id: select
143
+ name: Select changed workspaces
144
+ env:
145
+ WORKSPACES_JSON: ${{ vars.SOCKET_MONOREPO_WORKSPACES_JSON }}
146
+ BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
147
+ HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
148
+ shell: bash
149
+ run: |
150
+ python - <<'PY'
151
+ import fnmatch
152
+ import json
153
+ import os
154
+ import re
155
+ import subprocess
156
+
157
+ workspaces = json.loads(os.environ["WORKSPACES_JSON"])
158
+ if not isinstance(workspaces, list):
159
+ raise SystemExit("SOCKET_MONOREPO_WORKSPACES_JSON must be a JSON array")
160
+
161
+ base = os.environ["BASE_SHA"]
162
+ head = os.environ["HEAD_SHA"]
163
+ if not base or set(base) == {"0"}:
164
+ base = subprocess.check_output(
165
+ ["git", "rev-parse", f"{head}^"], text=True
166
+ ).strip()
167
+ changed_output = subprocess.check_output(
168
+ ["git", "diff", "--name-only", "-z", base, head]
169
+ )
170
+ changed = [
171
+ item.decode("utf-8", "surrogateescape")
172
+ for item in changed_output.split(b"\0")
173
+ if item
174
+ ]
175
+
176
+ selected = []
177
+ for workspace in workspaces:
178
+ name = workspace.get("name", "")
179
+ sub_paths = workspace.get("sub_paths") or []
180
+ watch_globs = workspace.get("watch_globs") or []
181
+ if not re.fullmatch(r"[A-Za-z0-9._-]+", name):
182
+ raise SystemExit(f"Invalid workspace name: {name!r}")
183
+ if not sub_paths or any(
184
+ not isinstance(path, str)
185
+ or path.startswith("/")
186
+ or ".." in path.split("/")
187
+ for path in sub_paths
188
+ ):
189
+ raise SystemExit(f"Invalid sub_paths for workspace {name!r}")
190
+ if not watch_globs:
191
+ watch_globs = [
192
+ pattern
193
+ for path in sub_paths
194
+ for pattern in (
195
+ ["*"]
196
+ if path.strip("/") in ("", ".")
197
+ else [path.rstrip("/"), f"{path.rstrip('/')}/*"]
198
+ )
199
+ ]
200
+ if any(
201
+ fnmatch.fnmatchcase(path, pattern)
202
+ for path in changed
203
+ for pattern in watch_globs
204
+ ):
205
+ selected.append({"name": name, "sub_paths": sub_paths})
206
+
207
+ matrix = json.dumps({"include": selected}, separators=(",", ":"))
208
+ with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
209
+ output.write(f"count={len(selected)}\n")
210
+ output.write(f"matrix={matrix}\n")
211
+ PY
212
+
213
+ scan-workspace:
214
+ needs: select-workspaces
215
+ if: needs.select-workspaces.outputs.count != '0'
216
+ timeout-minutes: 20
217
+ strategy:
218
+ fail-fast: false
219
+ matrix: ${{ fromJSON(needs.select-workspaces.outputs.matrix) }}
220
+ name: Socket scan (${{ matrix.name }})
221
+ runs-on: ubuntu-latest
222
+ steps:
223
+ - uses: actions/checkout@v5
224
+ with:
225
+ fetch-depth: 0
226
+ persist-credentials: false
227
+
228
+ - uses: actions/setup-python@v6
229
+ with:
230
+ python-version: '3.12'
231
+
232
+ - name: Install pinned Socket CLI
233
+ env:
234
+ SOCKETCLI_VERSION: ${{ vars.SOCKETCLI_VERSION }}
235
+ run: |
236
+ python -m pip install "socketsecurity==$SOCKETCLI_VERSION"
237
+ socketcli --version
238
+
239
+ - name: Scan workspace
240
+ env:
241
+ SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }}
242
+ PR_NUMBER: ${{ github.event.pull_request.number || 0 }}
243
+ WORKSPACE_NAME: ${{ matrix.name }}
244
+ SUB_PATHS_JSON: ${{ toJSON(matrix.sub_paths) }}
245
+ shell: bash
246
+ run: |
247
+ set +e
248
+ args=(
249
+ --target-path "$GITHUB_WORKSPACE"
250
+ --workspace-name "$WORKSPACE_NAME"
251
+ --enable-diff
252
+ --pr-number "$PR_NUMBER"
253
+ --exit-code-on-api-error 3
254
+ --report-link-file socket-report-link.txt
255
+ --summary-file socket-summary.txt
256
+ )
257
+ while IFS= read -r sub_path; do
258
+ args+=(--sub-path "$sub_path")
259
+ done < <(jq -r '.[]' <<<"$SUB_PATHS_JSON")
260
+
261
+ socketcli "${args[@]}" 2>&1 | tee socket-output.log
262
+ code=${PIPESTATUS[0]}
263
+
264
+ {
265
+ echo "## Socket scan: $WORKSPACE_NAME"
266
+ if [ -s socket-report-link.txt ]; then
267
+ echo "[View the report]($(cat socket-report-link.txt))"
268
+ fi
269
+ if [ -s socket-summary.txt ]; then
270
+ echo '```'
271
+ cat socket-summary.txt
272
+ echo '```'
273
+ fi
274
+ } >> "$GITHUB_STEP_SUMMARY"
275
+
276
+ exit "$code"
277
+
278
+ socket-security:
279
+ if: always()
280
+ needs: [select-workspaces, scan-workspace]
281
+ runs-on: ubuntu-latest
282
+ steps:
283
+ - name: Enforce matrix result
284
+ env:
285
+ SELECT_RESULT: ${{ needs.select-workspaces.result }}
286
+ SCAN_RESULT: ${{ needs.scan-workspace.result }}
287
+ run: |
288
+ test "$SELECT_RESULT" = success
289
+ [[ "$SCAN_RESULT" = success || "$SCAN_RESULT" = skipped ]]
290
+ ```
291
+
292
+ Each configuration object may intentionally contain several `sub_paths` when
293
+ those directories are one logical dependency graph. To split backend resolution,
294
+ use separate objects with different `name` values. Add `--workspace <name>` only
295
+ when the Socket organization requires API workspace association; it is not a scan
296
+ scope control. Use `--save-submitted-files-list` in a non-required canary to verify
297
+ the exact manifests selected before adopting workspace-level scans as a merge gate.
298
+
299
+ The job has an explicit 20-minute total budget. Tune that value from observed
300
+ workspace-level latency after the split; a five-minute cap can still be too close
301
+ to a slow request plus local startup. The CLI's `--timeout` is different: it
302
+ defaults to 1,200 seconds **per API request**. If an operator adds GNU `timeout`,
303
+ that process supervisor can terminate the CLI before it maps an error through
304
+ `--exit-code-on-api-error`; without `--preserve-status`, GNU reports 124 after its
305
+ initial timeout signal or 137 if `SIGKILL` is involved.
306
+
307
+ ### Buildkite
308
+
309
+ ```yaml
310
+ steps:
311
+ - label: "Socket scan"
312
+ command: "socketcli --config .socketcli.toml --target-path ."
313
+ env:
314
+ SOCKET_SECURITY_API_TOKEN: "${SOCKET_SECURITY_API_TOKEN}"
315
+ ```
316
+
317
+ The CLI reads Buildkite's native `BUILDKITE_COMMIT`, `BUILDKITE_BRANCH`,
318
+ `BUILDKITE_PULL_REQUEST`, and `BUILDKITE_PULL_REQUEST_BASE_BRANCH` variables.
319
+ For pull-request builds, ensure the checkout contains the base branch and the
320
+ checked-out head commit. The CLI uses those local refs first and performs a
321
+ targeted fetch only when a required ref or its comparison history is missing;
322
+ it does not fetch every remote ref and tag during startup.
323
+
324
+ When `--scm github` is used from Buildkite, the CLI also derives GitHub comment
325
+ context from `BUILDKITE_REPO`, `BUILDKITE_BUILD_CHECKOUT_PATH`, and the variables
326
+ above. Set `GH_API_TOKEN` to a GitHub token with the required repository access.
327
+ GitHub Enterprise users should also set `GITHUB_API_URL`; GitHub.com defaults to
328
+ `https://api.github.com`.
329
+
330
+ #### Merge-base baselines in Buildkite (dynamic pipelines)
331
+
332
+ Notes for using `--base-commit-sha` (see the
333
+ [merge-base note in the CLI reference](cli-reference.md#pull-request-and-commit))
334
+ when your steps are emitted by a
335
+ [dynamic pipeline](https://buildkite.com/docs/pipelines/configure/dynamic-pipelines)
336
+ generator rather than a static YAML file:
337
+
338
+ - **Compute the merge base at generation time, not step time.** The generator runs
339
+ with a full checkout; step agents may have shallow or fresh clones where
340
+ `git merge-base` fails or needs an extra fetch. Resolve it once in the generator and
341
+ bake it into the emitted step's `env`. Diff against the PR's *target* branch, which
342
+ isn't always the default branch (see Buildkite's
343
+ [environment variables](https://buildkite.com/docs/pipelines/configure/environment-variables)):
344
+
345
+ ```shell
346
+ TARGET="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-$BUILDKITE_PIPELINE_DEFAULT_BRANCH}"
347
+ BASE_SHA=$(git merge-base "origin/${TARGET}" HEAD)
348
+ ```
349
+
350
+ - **Emit the backfill step conditionally from the generator.** The generator is the
351
+ natural place for the "does a baseline scan exist?" check
352
+ (`GET /orgs/{org}/full-scans?repo=<repo>&commit_hash=$BASE_SHA&per_page=1`): only
353
+ emit the baseline-scan step when it returns nothing. The emitted pipeline then shows
354
+ in the UI whether a backfill will run.
355
+
356
+ - **Keep the backfill inside one command step.** The checkout-base → scan →
357
+ checkout-PR sequence must not be split across steps — steps can land on different
358
+ agents with different checkouts. Prefer
359
+ [`git worktree`](https://git-scm.com/docs/git-worktree) over mutating the step's
360
+ checkout: `git worktree add /tmp/socket-base "$BASE_SHA"` then
361
+ `socketcli --target-path /tmp/socket-base --branch "$TARGET" --disable-blocking`.
362
+
363
+ - **Soft-fail infra errors, not findings.** A missing baseline (or any API error)
364
+ exits with code 3 (`--exit-code-on-api-error` to change it); real findings exit 1.
365
+ [`soft_fail: [{exit_status: 3}]`](https://buildkite.com/docs/pipelines/configure/step-types/command-step)
366
+ on the PR scan step keeps infra errors from blocking merges while security findings
367
+ still do.
368
+
369
+ - **["Cancel intermediate builds"](https://buildkite.com/docs/pipelines/configure/canceling-builds#cancel-running-intermediate-builds)
370
+ on the default branch is the main source of baseline gaps.** Canceled builds never
371
+ scan their commit, so merge-base lookups for PRs based on those commits fail. The
372
+ conditional backfill step above is the remedy; there is no per-step exemption from
373
+ build cancellation in Buildkite. If you need strict scan-once semantics for
374
+ concurrent backfills of the same merge base, serialize the backfill step with a
375
+ [concurrency group](https://buildkite.com/docs/pipelines/configure/workflows/controlling-concurrency)
376
+ keyed on the merge-base SHA.
377
+
378
+ ### GitLab CI
379
+
380
+ ```yaml
381
+ socket_scan:
382
+ script:
383
+ - socketcli --config .socketcli.toml --target-path .
384
+ variables:
385
+ SOCKET_SECURITY_API_TOKEN: $SOCKET_SECURITY_API_TOKEN
386
+ ```
387
+
388
+ ### Bitbucket Pipelines
389
+
390
+ ```yaml
391
+ pipelines:
392
+ default:
393
+ - step:
394
+ script:
395
+ - socketcli --config .socketcli.toml --target-path .
396
+ ```
397
+
398
+ ## Workflow templates
399
+
400
+ Prebuilt examples in this repo:
401
+
402
+ - [`../workflows/github-actions.yml`](../workflows/github-actions.yml)
403
+ - [`../workflows/buildkite.yml`](../workflows/buildkite.yml)
404
+ - [`../workflows/gitlab-ci.yml`](../workflows/gitlab-ci.yml)
405
+ - [`../workflows/bitbucket-pipelines.yml`](../workflows/bitbucket-pipelines.yml)
406
+
407
+ ## CI gotchas
408
+
409
+ - `--strict-blocking` enables strict diff behavior (`new + unchanged`) for blocking evaluation and diff-based output selection.
410
+ - `--sarif-scope full` requires `--reach`.
411
+ - `--sarif-grouping alert` currently applies to `--sarif-scope full`.
412
+ - Diff-based SARIF can validly be empty when there are no matching net-new alerts.
413
+ - Keep API tokens in secret stores (`SOCKET_SECURITY_API_TOKEN`), not in config files.