socketsecurity 2.5.9__tar.gz → 2.6.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 (157) hide show
  1. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/actions/setup-docker/action.yml +5 -0
  2. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/dependency-review.yml +4 -1
  3. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/e2e-test.yml +48 -1
  4. socketsecurity-2.6.1/.github/workflows/package-check.yml +66 -0
  5. socketsecurity-2.6.1/.github/workflows/pr-preview.yml +306 -0
  6. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/release.yml +21 -7
  7. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.hooks/sync_version.py +34 -2
  8. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/CHANGELOG.md +46 -0
  9. socketsecurity-2.6.1/CONTRIBUTING.md +62 -0
  10. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/Dockerfile +27 -7
  11. socketsecurity-2.6.1/Dockerfile.preview +68 -0
  12. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/PKG-INFO +12 -12
  13. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/pyproject.toml +13 -13
  14. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/__init__.py +1 -1
  15. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/__init__.py +183 -30
  16. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/conftest.py +21 -0
  17. socketsecurity-2.6.1/tests/core/test_diff_scan_polling.py +122 -0
  18. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_sdk_methods.py +26 -12
  19. socketsecurity-2.6.1/tests/e2e/reach-facts-probe.sh +19 -0
  20. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/validate-reachability.sh +19 -0
  21. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/uv.lock +16 -28
  22. socketsecurity-2.5.9/.github/workflows/pr-preview.yml +0 -169
  23. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/CODEOWNERS +0 -0
  24. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  25. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  26. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  27. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  28. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/actions/setup-hatch/action.yml +0 -0
  29. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/actions/setup-sfw/action.yml +0 -0
  30. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/dependabot.yml +0 -0
  31. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/docker-stable.yml +0 -0
  32. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/python-tests.yml +0 -0
  33. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/workflows/version-check.yml +0 -0
  34. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.github/zizmor.yml +0 -0
  35. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.gitignore +0 -0
  36. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.pre-commit-config.yaml +0 -0
  37. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/.python-version +0 -0
  38. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/LICENSE +0 -0
  39. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/Makefile +0 -0
  40. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/README.md +0 -0
  41. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/docs/ci-cd.md +0 -0
  42. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/docs/cli-reference.md +0 -0
  43. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/docs/development.md +0 -0
  44. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/docs/troubleshooting.md +0 -0
  45. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-dashboard-parity.json +0 -0
  46. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-dashboard-parity.toml +0 -0
  47. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-diff-ci-cd.json +0 -0
  48. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-diff-ci-cd.toml +0 -0
  49. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-instance-detail.json +0 -0
  50. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/examples/config/sarif-instance-detail.toml +0 -0
  51. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/instructions/gitlab-commit-status/uat.md +0 -0
  52. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/pytest.ini +0 -0
  53. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/build_container.sh +0 -0
  54. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/build_container_flexible.sh +0 -0
  55. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/deploy-test-docker.sh +0 -0
  56. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/deploy-test-pypi.sh +0 -0
  57. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/docker-entrypoint.sh +0 -0
  58. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/scripts/run.sh +0 -0
  59. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/session.md +0 -0
  60. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socket.yml +0 -0
  61. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/config.py +0 -0
  62. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/alert_selection.py +0 -0
  63. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/classes.py +0 -0
  64. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/cli_client.py +0 -0
  65. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/cli_run.py +0 -0
  66. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/exceptions.py +0 -0
  67. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/git_interface.py +0 -0
  68. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/helper/__init__.py +0 -0
  69. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  70. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/lazy_file_loader.py +0 -0
  71. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/log_uploader.py +0 -0
  72. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/logging.py +0 -0
  73. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/messages.py +0 -0
  74. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/resource_utils.py +0 -0
  75. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm/__init__.py +0 -0
  76. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm/base.py +0 -0
  77. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm/client.py +0 -0
  78. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm/github.py +0 -0
  79. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm/gitlab.py +0 -0
  80. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/scm_comments.py +0 -0
  81. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/socket_config.py +0 -0
  82. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/streaming.py +0 -0
  83. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/tools/reachability.py +0 -0
  84. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/core/utils.py +0 -0
  85. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/fossa_compat.py +0 -0
  86. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/output.py +0 -0
  87. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/__init__.py +0 -0
  88. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/base.py +0 -0
  89. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/formatters/__init__.py +0 -0
  90. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/formatters/slack.py +0 -0
  91. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/jira.py +0 -0
  92. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/manager.py +0 -0
  93. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/slack.py +0 -0
  94. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/teams.py +0 -0
  95. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/plugins/webhook.py +0 -0
  96. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/socketsecurity/socketcli.py +0 -0
  97. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/__init__.py +0 -0
  98. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/create_diff_input.json +0 -0
  99. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_diff_alerts.py +0 -0
  100. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_diff_generation.py +0 -0
  101. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_facts_compression.py +0 -0
  102. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_has_manifest_files.py +0 -0
  103. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_package_and_alerts.py +0 -0
  104. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/core/test_supporting_methods.py +0 -0
  105. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/create_response.json +0 -0
  106. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/diff/stream_diff.json +0 -0
  107. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  108. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/head_scan/metadata.json +0 -0
  109. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  110. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  111. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/new_scan/metadata.json +0 -0
  112. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  113. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/repos/repo_info_error.json +0 -0
  114. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/repos/repo_info_no_head.json +0 -0
  115. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/repos/repo_info_success.json +0 -0
  116. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/data/settings/security-policy.json +0 -0
  117. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  118. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  119. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  120. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/validate-gitlab.sh +0 -0
  121. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/validate-json.sh +0 -0
  122. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/validate-sarif.sh +0 -0
  123. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/e2e/validate-scan.sh +0 -0
  124. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/fixtures/fossa/README.md +0 -0
  125. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  126. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  127. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  128. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  129. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/__init__.py +0 -0
  130. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_alert_selection.py +0 -0
  131. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_cli_config.py +0 -0
  132. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_cli_run.py +0 -0
  133. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_client.py +0 -0
  134. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_config.py +0 -0
  135. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_dependency_overview.py +0 -0
  136. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_disable_ignore.py +0 -0
  137. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_exclude_paths.py +0 -0
  138. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_fossa_compat.py +0 -0
  139. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_fossa_parity.py +0 -0
  140. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_full_scan_retry.py +0 -0
  141. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_gitlab_auth.py +0 -0
  142. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  143. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_gitlab_commit_status.py +0 -0
  144. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_gitlab_format.py +0 -0
  145. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  146. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_include_dirs.py +0 -0
  147. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_log_uploader.py +0 -0
  148. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_output.py +0 -0
  149. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_reachability.py +0 -0
  150. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_slack_plugin.py +0 -0
  151. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_socketcli.py +0 -0
  152. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_streaming.py +0 -0
  153. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/tests/unit/test_tier1_finalize.py +0 -0
  154. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/workflows/bitbucket-pipelines.yml +0 -0
  155. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/workflows/buildkite.yml +0 -0
  156. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/workflows/github-actions.yml +0 -0
  157. {socketsecurity-2.5.9 → socketsecurity-2.6.1}/workflows/gitlab-ci.yml +0 -0
@@ -5,6 +5,10 @@ description: >-
5
5
  preview, and stable workflows.
6
6
 
7
7
  inputs:
8
+ enable-qemu:
9
+ description: "Set up QEMU for multi-platform builds"
10
+ required: false
11
+ default: "true"
8
12
  dockerhub-username:
9
13
  description: "Docker Hub username (pass from secrets)"
10
14
  required: true
@@ -16,6 +20,7 @@ runs:
16
20
  using: "composite"
17
21
  steps:
18
22
  - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
23
+ if: inputs.enable-qemu == 'true'
19
24
  - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
20
25
  - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
21
26
  with:
@@ -571,7 +571,10 @@ jobs:
571
571
  persist-credentials: false
572
572
 
573
573
  - name: Build the Dockerfile (no push)
574
- run: docker build --pull -t socket-python-cli:dependabot-smoke .
574
+ run: >-
575
+ docker build --pull
576
+ --build-arg USE_LOCAL_INSTALL=true
577
+ -t socket-python-cli:dependabot-smoke .
575
578
 
576
579
  workflow-notice:
577
580
  needs: inspect
@@ -46,6 +46,11 @@ jobs:
46
46
  --enable-debug
47
47
  validate: tests/e2e/validate-reachability.sh
48
48
  setup-node: "true"
49
+ # The tier-1 reachability backend intermittently returns empty
50
+ # results while the CLI reports success. The probe exits 0 when
51
+ # the facts file has alerted components; anything else is
52
+ # retried before validation fails the job.
53
+ retry-probe: bash tests/e2e/reach-facts-probe.sh tests/e2e/fixtures/simple-npm
49
54
 
50
55
  - name: gitlab
51
56
  args: >-
@@ -94,17 +99,59 @@ jobs:
94
99
  run: pip install uv
95
100
 
96
101
  - name: Run Socket CLI
102
+ id: run-cli
97
103
  env:
98
104
  SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
105
+ RETRY_PROBE: ${{ matrix.retry-probe }}
99
106
  run: |
100
107
  set -o pipefail
101
- socketcli ${{ matrix.args }} 2>&1 | tee /tmp/e2e-output.log
108
+ # Entries with retry-probe get up to 3 attempts: the probe exits 0
109
+ # when the scan output looks complete, and a run that fails it is
110
+ # re-run on the assumption of a transient backend failure. A
111
+ # persistent incomplete result still reaches validation. Validation
112
+ # only treats the explicit zero-project backend signature as
113
+ # inconclusive; any other empty result remains a failure. Retries are
114
+ # surfaced as warning annotations so flake frequency stays visible.
115
+ max_attempts=3
116
+ attempt=1
117
+ while :; do
118
+ socketcli ${{ matrix.args }} 2>&1 | tee /tmp/e2e-output.log
119
+ [ -z "$RETRY_PROBE" ] && break
120
+ if bash -c "$RETRY_PROBE"; then
121
+ break
122
+ fi
123
+ if [ "$attempt" -ge "$max_attempts" ]; then
124
+ echo "::warning title=e2e-${{ matrix.name }} incomplete results::output still fails the completeness probe after ${max_attempts} attempts; letting validation classify the result"
125
+ echo "diagnostics=true" >> "$GITHUB_OUTPUT"
126
+ break
127
+ fi
128
+ echo "::warning title=e2e-${{ matrix.name }} transient retry::attempt ${attempt} failed the completeness probe (suspected backend transient); retrying"
129
+ echo "e2e-${{ matrix.name }}: retry after attempt ${attempt} — completeness probe failed (suspected transient)" >> "$GITHUB_STEP_SUMMARY"
130
+ attempt=$((attempt+1))
131
+ sleep 30
132
+ done
102
133
 
103
134
  - name: Validate results
104
135
  env:
105
136
  SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
106
137
  run: bash ${{ matrix.validate }}
107
138
 
139
+ - name: Upload diagnostics on failure
140
+ if: failure() || steps.run-cli.outputs.diagnostics == 'true'
141
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
142
+ with:
143
+ name: e2e-${{ matrix.name }}-diagnostics-attempt${{ github.run_attempt }}
144
+ path: |
145
+ /tmp/e2e-output.log
146
+ /tmp/*.sarif
147
+ tests/e2e/fixtures/simple-npm/.socket.facts.json
148
+ tests/e2e/fixtures/simple-pypi/.socket.facts.json
149
+ gl-*.json
150
+ license_output.json
151
+ if-no-files-found: ignore
152
+ include-hidden-files: true
153
+ retention-days: 14
154
+
108
155
  # Branch protection requires the e2e-* checks, but the `e2e` job above is
109
156
  # skipped on PRs that can't access repository secrets -- fork PRs and
110
157
  # Dependabot PRs. A job skipped via a job-level `if` never expands its
@@ -0,0 +1,66 @@
1
+ name: Package Check
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: package-check-${{ github.event.pull_request.number || github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ package-check:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 10
20
+ steps:
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
+ with:
23
+ fetch-depth: 1
24
+ persist-credentials: false
25
+
26
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ - name: Install build tooling
31
+ uses: ./.github/actions/setup-hatch
32
+
33
+ - name: Install distribution validator
34
+ run: python -m pip install "twine>=4.0.0"
35
+
36
+ - name: Build distributions
37
+ run: hatch build
38
+
39
+ - name: Validate distributions
40
+ run: python -m twine check dist/*
41
+
42
+ - name: Install and inspect wheel without resolving dependencies
43
+ run: |
44
+ python -m venv "$RUNNER_TEMP/package-check"
45
+ "$RUNNER_TEMP/package-check/bin/pip" install --no-deps dist/*.whl
46
+ "$RUNNER_TEMP/package-check/bin/python" - <<'PY'
47
+ import compileall
48
+ import importlib.metadata
49
+ import pathlib
50
+ import sysconfig
51
+
52
+ distribution = importlib.metadata.distribution("socketsecurity")
53
+ entry_points = {entry_point.name for entry_point in distribution.entry_points}
54
+ assert "socketcli" in entry_points
55
+ package = pathlib.Path(sysconfig.get_paths()["purelib"]) / "socketsecurity"
56
+ assert compileall.compile_dir(package, quiet=1)
57
+ print("wheel metadata and bytecode smoke OK", distribution.version)
58
+ PY
59
+
60
+ - name: Upload distributions
61
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
62
+ with:
63
+ name: socketsecurity-${{ github.sha }}
64
+ path: dist/*
65
+ if-no-files-found: error
66
+ retention-days: 14
@@ -0,0 +1,306 @@
1
+ name: Publish PR Preview
2
+
3
+ on:
4
+ pull_request:
5
+ types: [labeled]
6
+ workflow_dispatch:
7
+ inputs:
8
+ pr_number:
9
+ description: Pull request number to publish
10
+ required: true
11
+ type: string
12
+ publish_test_pypi:
13
+ description: Publish the Python package to TestPyPI
14
+ required: true
15
+ default: true
16
+ type: boolean
17
+ publish_docker:
18
+ description: Publish socketdev/cli:pr-<number> to Docker Hub
19
+ required: true
20
+ default: false
21
+ type: boolean
22
+ sdk_preview_version:
23
+ description: Optional exact TestPyPI socketdev prerelease for the Docker image
24
+ required: false
25
+ type: string
26
+
27
+ concurrency:
28
+ group: publish-pr-preview-${{ github.event.pull_request.number || github.run_id }}
29
+ cancel-in-progress: false
30
+
31
+ jobs:
32
+ context:
33
+ if: >-
34
+ github.event_name == 'workflow_dispatch' ||
35
+ (github.event.label.name == 'publish-preview' ||
36
+ github.event.label.name == 'publish-docker-preview') &&
37
+ github.event.pull_request.head.repo.full_name == github.repository
38
+ runs-on: ubuntu-latest
39
+ timeout-minutes: 5
40
+ permissions:
41
+ contents: read
42
+ pull-requests: read
43
+ outputs:
44
+ pr_number: ${{ steps.context.outputs.pr_number }}
45
+ head_sha: ${{ steps.context.outputs.head_sha }}
46
+ steps:
47
+ - name: Validate pull request context
48
+ id: context
49
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
50
+ env:
51
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
52
+ EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
53
+ INPUT_PR_NUMBER: ${{ inputs.pr_number }}
54
+ WORKFLOW_REF: ${{ github.ref }}
55
+ with:
56
+ script: |
57
+ const rawPrNumber = context.eventName === 'workflow_dispatch'
58
+ ? process.env.INPUT_PR_NUMBER
59
+ : process.env.EVENT_PR_NUMBER;
60
+ if (!/^[1-9][0-9]*$/.test(rawPrNumber || '')) {
61
+ core.setFailed('Pull request number must contain ASCII digits only.');
62
+ return;
63
+ }
64
+
65
+ if (context.eventName === 'workflow_dispatch') {
66
+ const defaultRef = `refs/heads/${process.env.DEFAULT_BRANCH}`;
67
+ if (process.env.WORKFLOW_REF !== defaultRef) {
68
+ core.setFailed(`Run manual previews from ${defaultRef}.`);
69
+ return;
70
+ }
71
+ }
72
+
73
+ const prNumber = Number(rawPrNumber);
74
+ if (!Number.isSafeInteger(prNumber)) {
75
+ core.setFailed('Pull request number is outside the supported range.');
76
+ return;
77
+ }
78
+ const {data: pullRequest} = await github.rest.pulls.get({
79
+ owner: context.repo.owner,
80
+ repo: context.repo.repo,
81
+ pull_number: prNumber,
82
+ });
83
+ if (pullRequest.state !== 'open') {
84
+ core.setFailed(`Pull request #${prNumber} is not open.`);
85
+ return;
86
+ }
87
+ if (pullRequest.head.repo?.full_name !== `${context.repo.owner}/${context.repo.repo}`) {
88
+ core.setFailed('Preview publication is limited to branches in this repository.');
89
+ return;
90
+ }
91
+
92
+ core.setOutput('pr_number', String(prNumber));
93
+ core.setOutput('head_sha', pullRequest.head.sha);
94
+
95
+ build:
96
+ needs: context
97
+ runs-on: ubuntu-latest
98
+ timeout-minutes: 10
99
+ permissions:
100
+ contents: read
101
+ outputs:
102
+ preview_version: ${{ steps.version.outputs.preview_version }}
103
+ steps:
104
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
105
+ with:
106
+ ref: ${{ needs.context.outputs.head_sha }}
107
+ fetch-depth: 0
108
+ persist-credentials: false
109
+
110
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
111
+ with:
112
+ python-version: "3.12"
113
+
114
+ - name: Install build tooling
115
+ uses: ./.github/actions/setup-hatch
116
+
117
+ - name: Install distribution validator
118
+ run: python -m pip install "twine>=4.0.0"
119
+
120
+ - name: Inject deterministic preview version
121
+ env:
122
+ PREVIEW_ID: ${{ github.run_id }}
123
+ RUN_ATTEMPT: ${{ github.run_attempt }}
124
+ run: |
125
+ PREVIEW_ID=$((PREVIEW_ID * 100 + RUN_ATTEMPT))
126
+ python .hooks/sync_version.py --dev --preview-id "$PREVIEW_ID" --skip-lock
127
+
128
+ - name: Read preview version
129
+ id: version
130
+ run: echo "preview_version=$(hatch version)" >> "$GITHUB_OUTPUT"
131
+
132
+ - name: Build and validate distributions
133
+ run: |
134
+ hatch build
135
+ python -m twine check dist/*
136
+
137
+ - name: Install and inspect wheel locally
138
+ run: |
139
+ python -m venv "$RUNNER_TEMP/preview-check"
140
+ "$RUNNER_TEMP/preview-check/bin/pip" install --no-deps dist/*.whl
141
+ "$RUNNER_TEMP/preview-check/bin/python" - <<'PY'
142
+ import compileall
143
+ import importlib.metadata
144
+ import pathlib
145
+ import sysconfig
146
+
147
+ distribution = importlib.metadata.distribution("socketsecurity")
148
+ entry_points = {entry_point.name for entry_point in distribution.entry_points}
149
+ assert "socketcli" in entry_points
150
+ package = pathlib.Path(sysconfig.get_paths()["purelib"]) / "socketsecurity"
151
+ assert compileall.compile_dir(package, quiet=1)
152
+ print("preview wheel smoke OK", distribution.version)
153
+ PY
154
+
155
+ - name: Upload preview distributions
156
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
157
+ with:
158
+ name: socketsecurity-preview-${{ github.run_id }}-${{ github.run_attempt }}
159
+ path: dist/*
160
+ if-no-files-found: error
161
+ retention-days: 14
162
+
163
+ publish-package:
164
+ needs: [context, build]
165
+ if: >-
166
+ github.event.label.name == 'publish-preview' ||
167
+ (github.event_name == 'workflow_dispatch' && inputs.publish_test_pypi)
168
+ runs-on: ubuntu-latest
169
+ timeout-minutes: 10
170
+ permissions:
171
+ contents: read
172
+ id-token: write
173
+ pull-requests: write
174
+ steps:
175
+ - name: Download preview distributions
176
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
177
+ with:
178
+ name: socketsecurity-preview-${{ github.run_id }}-${{ github.run_attempt }}
179
+ path: dist
180
+
181
+ - name: Publish to TestPyPI
182
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
183
+ with:
184
+ repository-url: https://test.pypi.org/legacy/
185
+ verbose: true
186
+
187
+ - name: Comment on pull request
188
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
189
+ env:
190
+ PREVIEW_VERSION: ${{ needs.build.outputs.preview_version }}
191
+ PR_NUMBER: ${{ needs.context.outputs.pr_number }}
192
+ with:
193
+ script: |
194
+ const marker = '<!-- socketsecurity-pr-preview -->';
195
+ const prNumber = Number(process.env.PR_NUMBER);
196
+ const version = process.env.PREVIEW_VERSION;
197
+ const body = `${marker}
198
+ 🚀 CLI preview published: \`socketsecurity==${version}\`
199
+
200
+ \`\`\`bash
201
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple socketsecurity==${version}
202
+ \`\`\`
203
+
204
+ TestPyPI's package index can take several minutes to expose a newly uploaded version.`;
205
+ const {data: comments} = await github.rest.issues.listComments({
206
+ owner: context.repo.owner,
207
+ repo: context.repo.repo,
208
+ issue_number: prNumber,
209
+ });
210
+ const existing = comments.find(comment =>
211
+ comment.user.type === 'Bot' && comment.body.includes(marker)
212
+ );
213
+ if (existing) {
214
+ await github.rest.issues.updateComment({
215
+ owner: context.repo.owner,
216
+ repo: context.repo.repo,
217
+ comment_id: existing.id,
218
+ body,
219
+ });
220
+ } else {
221
+ await github.rest.issues.createComment({
222
+ owner: context.repo.owner,
223
+ repo: context.repo.repo,
224
+ issue_number: prNumber,
225
+ body,
226
+ });
227
+ }
228
+
229
+ publish-docker:
230
+ needs: [context, build]
231
+ if: >-
232
+ github.event.label.name == 'publish-docker-preview' ||
233
+ (github.event_name == 'workflow_dispatch' && inputs.publish_docker)
234
+ runs-on: ubuntu-latest
235
+ timeout-minutes: 15
236
+ permissions:
237
+ contents: read
238
+ pull-requests: write
239
+ steps:
240
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
241
+ with:
242
+ # Keep the Dockerfile and credential-handling action on trusted code.
243
+ # The pull request enters this job only through the built wheel.
244
+ ref: ${{ github.event.repository.default_branch }}
245
+ fetch-depth: 1
246
+ persist-credentials: false
247
+
248
+ - name: Download preview distributions
249
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
250
+ with:
251
+ name: socketsecurity-preview-${{ github.run_id }}-${{ github.run_attempt }}
252
+ path: dist
253
+
254
+ - name: Set up Docker publishing
255
+ uses: ./.github/actions/setup-docker
256
+ with:
257
+ enable-qemu: "false"
258
+ dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
259
+ dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
260
+
261
+ - name: Build and push Docker preview
262
+ uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
263
+ with:
264
+ file: Dockerfile.preview
265
+ push: true
266
+ pull: true
267
+ platforms: linux/amd64
268
+ tags: socketdev/cli:pr-${{ needs.context.outputs.pr_number }}
269
+ build-args: |
270
+ SDK_PREVIEW_VERSION=${{ inputs.sdk_preview_version }}
271
+
272
+ - name: Comment on pull request
273
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
274
+ env:
275
+ PR_NUMBER: ${{ needs.context.outputs.pr_number }}
276
+ with:
277
+ script: |
278
+ const marker = '<!-- socketsecurity-docker-preview -->';
279
+ const prNumber = Number(process.env.PR_NUMBER);
280
+ const body = `${marker}
281
+ 🐳 Docker preview published: \`socketdev/cli:pr-${prNumber}\`
282
+
283
+ This mutable tag is only created when a Docker preview is explicitly requested.`;
284
+ const {data: comments} = await github.rest.issues.listComments({
285
+ owner: context.repo.owner,
286
+ repo: context.repo.repo,
287
+ issue_number: prNumber,
288
+ });
289
+ const existing = comments.find(comment =>
290
+ comment.user.type === 'Bot' && comment.body.includes(marker)
291
+ );
292
+ if (existing) {
293
+ await github.rest.issues.updateComment({
294
+ owner: context.repo.owner,
295
+ repo: context.repo.repo,
296
+ comment_id: existing.id,
297
+ body,
298
+ });
299
+ } else {
300
+ await github.rest.issues.createComment({
301
+ owner: context.repo.owner,
302
+ repo: context.repo.repo,
303
+ issue_number: prNumber,
304
+ body,
305
+ });
306
+ }
@@ -79,17 +79,31 @@ jobs:
79
79
  env:
80
80
  VERSION: ${{ env.VERSION }}
81
81
  run: |
82
- for i in {1..30}; do
83
- if pip install socketsecurity==${VERSION}; then
82
+ # The first lookup can race PyPI's Simple-index propagation, and a delayed
83
+ # CDN purge can leave the index stale well after a successful upload.
84
+ # pip caches HTTP responses by default, so without --no-cache-dir every
85
+ # retry can reuse that initial stale response instead of checking whether
86
+ # the release has appeared. Budget: 30 minutes.
87
+ MAX_ATTEMPTS=60
88
+ for i in $(seq 1 "$MAX_ATTEMPTS"); do
89
+ if python -m pip install \
90
+ --no-cache-dir \
91
+ --index-url https://pypi.org/simple/ \
92
+ "socketsecurity==${VERSION}"; then
84
93
  echo "Package ${VERSION} is now available and installable on PyPI"
85
- pip uninstall -y socketsecurity
86
- echo "success=true" >> $GITHUB_OUTPUT
94
+ python -m pip uninstall -y socketsecurity
95
+ echo "success=true" >> "$GITHUB_OUTPUT"
87
96
  exit 0
88
97
  fi
89
- echo "Attempt $i: Package not yet installable, waiting 20s... (${i}/30)"
90
- sleep 20
98
+ if curl -s -f "https://pypi.org/pypi/socketsecurity/${VERSION}/json" > /dev/null; then
99
+ echo "Release ${VERSION} exists on PyPI (JSON API) but is not in the Simple index yet - CDN propagation delay"
100
+ fi
101
+ if [ "$i" -lt "$MAX_ATTEMPTS" ]; then
102
+ echo "Attempt $i: Package not yet installable, waiting 30s... (${i}/${MAX_ATTEMPTS})"
103
+ sleep 30
104
+ fi
91
105
  done
92
- echo "success=false" >> $GITHUB_OUTPUT
106
+ echo "success=false" >> "$GITHUB_OUTPUT"
93
107
  exit 1
94
108
 
95
109
  - name: Build & Push Docker
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env python3
2
- import subprocess
2
+ import json
3
3
  import pathlib
4
4
  import re
5
+ import subprocess
5
6
  import sys
6
7
  import urllib.request
7
- import json
8
8
 
9
9
  INIT_FILE = pathlib.Path("socketsecurity/__init__.py")
10
10
  PYPROJECT_FILE = pathlib.Path("pyproject.toml")
@@ -125,13 +125,45 @@ def run_uv_lock() -> bool:
125
125
  after = UV_LOCK_FILE.read_bytes() if UV_LOCK_FILE.exists() else b""
126
126
  return before != after
127
127
 
128
+
129
+ def read_preview_id():
130
+ if "--preview-id" not in sys.argv:
131
+ return None
132
+
133
+ option_index = sys.argv.index("--preview-id")
134
+ try:
135
+ preview_id = sys.argv[option_index + 1]
136
+ except IndexError:
137
+ print("❌ `--preview-id` requires a numeric value.")
138
+ sys.exit(1)
139
+
140
+ if not preview_id.isascii() or not preview_id.isdigit():
141
+ print("❌ `--preview-id` must contain ASCII digits only.")
142
+ sys.exit(1)
143
+ return preview_id
144
+
145
+
128
146
  def main():
129
147
  dev_mode = "--dev" in sys.argv
148
+ skip_lock = "--skip-lock" in sys.argv
149
+ preview_id = read_preview_id()
130
150
  current_version = read_version_from_init(INIT_FILE)
131
151
  previous_version = read_version_from_git("socketsecurity/__init__.py")
132
152
 
133
153
  print(f"Current: {current_version}, Previous: {previous_version}")
134
154
 
155
+ if preview_id is not None:
156
+ if not dev_mode:
157
+ print("❌ `--preview-id` can only be used with `--dev`.")
158
+ sys.exit(1)
159
+ base_version = current_version.split(".dev")[0]
160
+ new_version = f"{base_version}.dev{preview_id}"
161
+ inject_version(new_version)
162
+ if not skip_lock:
163
+ run_uv_lock()
164
+ print(f"✅ Prepared deterministic preview version {new_version}.")
165
+ sys.exit(0)
166
+
135
167
  if current_version == previous_version:
136
168
  if dev_mode:
137
169
  base_version = current_version.split(".dev")[0] if ".dev" in current_version else current_version
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.6.1
4
+
5
+ ### Changed: scan comparison now polls the diff-scans endpoints
6
+
7
+ - Diff mode no longer holds a single idle HTTP connection open while the API
8
+ computes the scan comparison. The CLI now creates a diff-scan resource
9
+ (`POST /orgs/{org}/diff-scans/from-ids`) and polls
10
+ `GET /orgs/{org}/diff-scans/{id}?cached=true` with short, bounded requests
11
+ until the comparison is ready (HTTP 200 instead of 202). This fixes
12
+ intermittent `Connection reset by peer` failures on the final comparison
13
+ step when scans take several minutes to compare and network middleboxes
14
+ (e.g. Azure NAT gateways, which default to a 4-minute TCP idle timeout)
15
+ reap the idle connection.
16
+ - Duplicate scan pairs are resolved after an HTTP 409 and then polled through
17
+ the same cached endpoint. This avoids automatically following the API's 302
18
+ duplicate redirect with an uncached, potentially long-lived GET request.
19
+ - The change is transparent: no flags or workflow changes are needed. If the
20
+ org API token is missing the `diff-scans:create`, `diff-scans:list` or
21
+ `full-scans:list` scopes — or the new flow fails for any other reason — the
22
+ CLI logs a warning and falls back to the legacy streaming comparison.
23
+ - Requires the pinned `socketdev==3.5.0` SDK.
24
+
25
+ ## 2.6.0
26
+
27
+ ### Changed: pin all Python dependencies
28
+
29
+ - Pinned every runtime dependency in `pyproject.toml` to an exact version;
30
+ several were previously unpinned or open ranges.
31
+ - Replaced the `bs4` shim package with a direct, pinned `beautifulsoup4`
32
+ dependency (the shim provided no version control over the actual library).
33
+ - Pinned the bundled `socketdev` SDK to `3.5.0` (previously `>=3.3.0,<4.0.0`).
34
+ - Docker images now install Python dependencies from the committed `uv.lock`
35
+ with pip hash verification (`--require-hashes`), so image builds no longer
36
+ resolve dependency versions from PyPI at build time. `pip check` validates
37
+ the environment after install.
38
+ - Pinned the `hatchling` build backend and the `uv` binary used in the
39
+ Dockerfile.
40
+
41
+ ### Changed: e2e reachability jobs retry transient empty results
42
+
43
+ - Reachability e2e runs that report success with no alerted components in the
44
+ facts file are retried up to three times as a suspected transient backend
45
+ failure. After retries, only the known zero-project backend signature is
46
+ classified as inconclusive — any other empty result still fails — and e2e
47
+ jobs upload their logs and reports as diagnostics on failure.
48
+
3
49
  ## 2.5.9
4
50
 
5
51
  ### Changed: bump pinned @coana-tech/cli to 15.10.3