socketsecurity 2.3.0__tar.gz → 2.4.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 (141) hide show
  1. socketsecurity-2.4.0/.github/actions/setup-docker/action.yml +23 -0
  2. socketsecurity-2.4.0/.github/actions/setup-hatch/action.yml +13 -0
  3. socketsecurity-2.4.0/.github/actions/setup-sfw/action.yml +49 -0
  4. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/dependabot.yml +4 -2
  5. socketsecurity-2.4.0/.github/workflows/dependency-review.yml +588 -0
  6. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/docker-stable.yml +8 -12
  7. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/e2e-test.yml +4 -4
  8. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/pr-preview.yml +30 -23
  9. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/python-tests.yml +4 -4
  10. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/release.yml +13 -21
  11. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/workflows/version-check.yml +6 -2
  12. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/CHANGELOG.md +50 -28
  13. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/PKG-INFO +4 -2
  14. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/pyproject.toml +4 -2
  15. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/__init__.py +1 -1
  16. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/config.py +6 -1
  17. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/__init__.py +161 -4
  18. socketsecurity-2.4.0/tests/core/test_facts_compression.py +137 -0
  19. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_sdk_methods.py +18 -2
  20. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/uv.lock +112 -6
  21. socketsecurity-2.3.0/.github/workflows/dependabot-review.yml +0 -205
  22. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/CODEOWNERS +0 -0
  23. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  24. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  25. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  26. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  27. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.github/zizmor.yml +0 -0
  28. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.gitignore +0 -0
  29. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.hooks/sync_version.py +0 -0
  30. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.pre-commit-config.yaml +0 -0
  31. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/.python-version +0 -0
  32. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/Dockerfile +0 -0
  33. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/LICENSE +0 -0
  34. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/Makefile +0 -0
  35. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/README.md +0 -0
  36. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/docs/ci-cd.md +0 -0
  37. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/docs/cli-reference.md +0 -0
  38. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/docs/development.md +0 -0
  39. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/docs/troubleshooting.md +0 -0
  40. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-dashboard-parity.json +0 -0
  41. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-dashboard-parity.toml +0 -0
  42. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-diff-ci-cd.json +0 -0
  43. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-diff-ci-cd.toml +0 -0
  44. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-instance-detail.json +0 -0
  45. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/examples/config/sarif-instance-detail.toml +0 -0
  46. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/instructions/gitlab-commit-status/uat.md +0 -0
  47. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/pytest.ini +0 -0
  48. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/build_container.sh +0 -0
  49. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/build_container_flexible.sh +0 -0
  50. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/deploy-test-docker.sh +0 -0
  51. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/deploy-test-pypi.sh +0 -0
  52. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/docker-entrypoint.sh +0 -0
  53. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/scripts/run.sh +0 -0
  54. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/session.md +0 -0
  55. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socket.yml +0 -0
  56. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/alert_selection.py +0 -0
  57. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/classes.py +0 -0
  58. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/cli_client.py +0 -0
  59. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/exceptions.py +0 -0
  60. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/git_interface.py +0 -0
  61. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/helper/__init__.py +0 -0
  62. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  63. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/lazy_file_loader.py +0 -0
  64. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/logging.py +0 -0
  65. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/messages.py +0 -0
  66. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/resource_utils.py +0 -0
  67. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm/__init__.py +0 -0
  68. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm/base.py +0 -0
  69. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm/client.py +0 -0
  70. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm/github.py +0 -0
  71. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm/gitlab.py +0 -0
  72. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/scm_comments.py +0 -0
  73. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/socket_config.py +0 -0
  74. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/tools/reachability.py +0 -0
  75. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/core/utils.py +0 -0
  76. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/fossa_compat.py +0 -0
  77. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/output.py +0 -0
  78. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/__init__.py +0 -0
  79. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/base.py +0 -0
  80. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/formatters/__init__.py +0 -0
  81. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/formatters/slack.py +0 -0
  82. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/jira.py +0 -0
  83. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/manager.py +0 -0
  84. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/slack.py +0 -0
  85. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/teams.py +0 -0
  86. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/plugins/webhook.py +0 -0
  87. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/socketsecurity/socketcli.py +0 -0
  88. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/__init__.py +0 -0
  89. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/conftest.py +0 -0
  90. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/create_diff_input.json +0 -0
  91. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_diff_alerts.py +0 -0
  92. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_diff_generation.py +0 -0
  93. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_has_manifest_files.py +0 -0
  94. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_package_and_alerts.py +0 -0
  95. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/core/test_supporting_methods.py +0 -0
  96. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/create_response.json +0 -0
  97. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/diff/stream_diff.json +0 -0
  98. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  99. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/head_scan/metadata.json +0 -0
  100. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  101. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  102. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/new_scan/metadata.json +0 -0
  103. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  104. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/repos/repo_info_error.json +0 -0
  105. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/repos/repo_info_no_head.json +0 -0
  106. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/repos/repo_info_success.json +0 -0
  107. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/data/settings/security-policy.json +0 -0
  108. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  109. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  110. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  111. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/validate-gitlab.sh +0 -0
  112. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/validate-json.sh +0 -0
  113. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/validate-reachability.sh +0 -0
  114. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/validate-sarif.sh +0 -0
  115. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/e2e/validate-scan.sh +0 -0
  116. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/fixtures/fossa/README.md +0 -0
  117. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  118. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  119. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  120. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  121. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/__init__.py +0 -0
  122. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_alert_selection.py +0 -0
  123. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_cli_config.py +0 -0
  124. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_client.py +0 -0
  125. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_config.py +0 -0
  126. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_dependency_overview.py +0 -0
  127. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_disable_ignore.py +0 -0
  128. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_fossa_compat.py +0 -0
  129. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_fossa_parity.py +0 -0
  130. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_gitlab_auth.py +0 -0
  131. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  132. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_gitlab_commit_status.py +0 -0
  133. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_gitlab_format.py +0 -0
  134. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  135. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_output.py +0 -0
  136. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_slack_plugin.py +0 -0
  137. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/tests/unit/test_socketcli.py +0 -0
  138. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/workflows/bitbucket-pipelines.yml +0 -0
  139. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/workflows/buildkite.yml +0 -0
  140. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/workflows/github-actions.yml +0 -0
  141. {socketsecurity-2.3.0 → socketsecurity-2.4.0}/workflows/gitlab-ci.yml +0 -0
@@ -0,0 +1,23 @@
1
+ name: "Set up Docker"
2
+ description: >-
3
+ Set up QEMU + Docker Buildx and authenticate to Docker Hub for multi-arch
4
+ image builds. Centralizes the QEMU/Buildx/login trio used by release,
5
+ preview, and stable workflows.
6
+
7
+ inputs:
8
+ dockerhub-username:
9
+ description: "Docker Hub username (pass from secrets)"
10
+ required: true
11
+ dockerhub-token:
12
+ description: "Docker Hub token/password (pass from secrets)"
13
+ required: true
14
+
15
+ runs:
16
+ using: "composite"
17
+ steps:
18
+ - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
19
+ - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
20
+ - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
21
+ with:
22
+ username: ${{ inputs.dockerhub-username }}
23
+ password: ${{ inputs.dockerhub-token }}
@@ -0,0 +1,13 @@
1
+ name: "Set up Hatch build tooling"
2
+ description: >-
3
+ Install the pinned hatch / hatchling / virtualenv toolchain used to build
4
+ and publish the package. Assumes Python is already set up by the caller.
5
+
6
+ runs:
7
+ using: "composite"
8
+ steps:
9
+ - shell: bash
10
+ run: |
11
+ python -m pip install --upgrade pip
12
+ pip install "virtualenv<20.36"
13
+ pip install hatchling==1.27.0 hatch==1.14.0
@@ -0,0 +1,49 @@
1
+ name: "Set up Socket Firewall"
2
+ description: >-
3
+ Set up the requested language toolchain and install Socket Firewall (free
4
+ or enterprise edition) so subsequent steps can run package-manager commands
5
+ wrapped with `sfw`. Defaults to free/anonymous mode (no API token -- safe on
6
+ untrusted / Dependabot / fork PRs). Pass mode: firewall-enterprise +
7
+ socket-token for full org-policy enforcement on trusted maintainer PRs.
8
+
9
+ inputs:
10
+ python:
11
+ description: "Set up Python 3.12"
12
+ default: "false"
13
+ node:
14
+ description: "Set up Node 20 (needed for npm-wrapped checks)"
15
+ default: "false"
16
+ uv:
17
+ description: "Install uv (implies Python)"
18
+ default: "false"
19
+ mode:
20
+ description: "socketdev/action mode: firewall-free or firewall-enterprise"
21
+ default: "firewall-free"
22
+ socket-token:
23
+ description: "Socket API token (only used/required for firewall-enterprise)"
24
+ default: ""
25
+
26
+ runs:
27
+ using: "composite"
28
+ steps:
29
+ - if: ${{ inputs.python == 'true' || inputs.uv == 'true' }}
30
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
31
+ with:
32
+ python-version: "3.12"
33
+
34
+ - if: ${{ inputs.node == 'true' }}
35
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
36
+ with:
37
+ node-version: "20"
38
+
39
+ # Official Socket setup action. Wires up sfw routing correctly.
40
+ # socket-token is ignored in firewall-free mode and empty when absent.
41
+ - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2
42
+ with:
43
+ mode: ${{ inputs.mode }}
44
+ socket-token: ${{ inputs.socket-token }}
45
+
46
+ - if: ${{ inputs.uv == 'true' }}
47
+ name: Install uv
48
+ shell: bash
49
+ run: python -m pip install --upgrade pip uv
@@ -36,9 +36,11 @@ updates:
36
36
  cooldown:
37
37
  default-days: 7
38
38
 
39
- # GitHub Actions used in workflows
39
+ # GitHub Actions used in workflows and local composite actions.
40
40
  - package-ecosystem: "github-actions"
41
- directory: "/"
41
+ directories:
42
+ - "/"
43
+ - "/.github/actions/*"
42
44
  schedule:
43
45
  interval: "weekly"
44
46
  open-pull-requests-limit: 2
@@ -0,0 +1,588 @@
1
+ name: dependency-review
2
+
3
+ # Supply-chain guardrails for dependency-update PRs -- for BOTH Dependabot
4
+ # and maintainers.
5
+ #
6
+ # Inspects the changed files, then conditionally runs Socket Firewall (sfw)
7
+ # install smoke jobs for the affected manifests, picking the firewall edition
8
+ # per PR:
9
+ #
10
+ # - Trusted authors: any in-repo (non-fork) PR other than Dependabot's
11
+ # (i.e. someone with write access) -> Socket Firewall ENTERPRISE through
12
+ # the socket-firewall environment and its SOCKET_SFW_API_TOKEN secret
13
+ # (authenticated, full org-policy enforcement).
14
+ # - Everyone else: Dependabot and all fork PRs from external contributors ->
15
+ # Socket Firewall FREE (anonymous, no API token), which is safe in the
16
+ # unprivileged `pull_request` context.
17
+ #
18
+ # Only Enterprise jobs declare the socket-firewall environment. Free jobs do
19
+ # not touch that environment or its token.
20
+ #
21
+ # Each sfw smoke job collects an sfw-artifacts/ directory (provenance context
22
+ # + the firewall's console logs) and uploads it as a build artifact
23
+ # (if: always(), so the report survives even when sfw BLOCKS an install --
24
+ # which is exactly when you want to read it).
25
+ #
26
+ # Pattern adapted from SocketDev/socket-basics and SocketDev/socket-sdk-python.
27
+
28
+ on:
29
+ pull_request:
30
+ types: [opened, synchronize, reopened, ready_for_review]
31
+
32
+ permissions:
33
+ contents: read
34
+
35
+ concurrency:
36
+ group: dependency-review-${{ github.event.pull_request.number }}
37
+ cancel-in-progress: true
38
+
39
+ jobs:
40
+ inspect:
41
+ runs-on: ubuntu-latest
42
+ timeout-minutes: 5
43
+ outputs:
44
+ python_deps_changed: ${{ steps.diff.outputs.python_deps_changed }}
45
+ fixture_npm_changed: ${{ steps.diff.outputs.fixture_npm_changed }}
46
+ fixture_pypi_changed: ${{ steps.diff.outputs.fixture_pypi_changed }}
47
+ dockerfile_changed: ${{ steps.diff.outputs.dockerfile_changed }}
48
+ workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }}
49
+ sfw_mode: ${{ steps.mode.outputs.sfw_mode }}
50
+ steps:
51
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
52
+ with:
53
+ fetch-depth: 0
54
+ persist-credentials: false
55
+
56
+ - name: Inspect changed files
57
+ id: diff
58
+ env:
59
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
60
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
61
+ run: |
62
+ CHANGED_FILES="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")"
63
+
64
+ {
65
+ echo "## Changed files"
66
+ echo '```'
67
+ printf '%s\n' "$CHANGED_FILES"
68
+ echo '```'
69
+ } >> "$GITHUB_STEP_SUMMARY"
70
+
71
+ has_file() {
72
+ local pattern="$1"
73
+ if printf '%s\n' "$CHANGED_FILES" | grep -Eq "$pattern"; then
74
+ echo "true"
75
+ else
76
+ echo "false"
77
+ fi
78
+ }
79
+
80
+ {
81
+ echo "python_deps_changed=$(has_file '^(pyproject\.toml|uv\.lock)$')"
82
+ echo "fixture_npm_changed=$(has_file '^tests/e2e/fixtures/simple-npm/')"
83
+ echo "fixture_pypi_changed=$(has_file '^tests/e2e/fixtures/simple-pypi/')"
84
+ echo "dockerfile_changed=$(has_file '^Dockerfile$')"
85
+ echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')"
86
+ } >> "$GITHUB_OUTPUT"
87
+
88
+ - name: Determine Socket Firewall mode
89
+ id: mode
90
+ # Trusted == any in-repo (non-fork) PR that isn't Dependabot's. Only
91
+ # accounts with write access can push a branch to this repo, so a
92
+ # non-fork PR already implies a trusted author -- the same boundary
93
+ # GitHub uses to decide whether secrets are exposed at all.
94
+ #
95
+ # NB: author_association is deliberately NOT used to require strict org
96
+ # membership. It only reflects PUBLIC org membership, so private members
97
+ # (the common case) show up as CONTRIBUTOR and would be misclassified.
98
+ # Reliable strict-membership detection would need a read:org token or
99
+ # public membership. This step references NO secret regardless -- it
100
+ # only decides which smoke job runs.
101
+ env:
102
+ IS_DEPENDABOT: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
103
+ IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
104
+ AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }}
105
+ run: |
106
+ mode=firewall-free
107
+ if [ "$IS_DEPENDABOT" != "true" ] && [ "$IS_FORK" != "true" ]; then
108
+ mode=firewall-enterprise
109
+ fi
110
+
111
+ echo "sfw_mode=$mode" >> "$GITHUB_OUTPUT"
112
+ {
113
+ echo "## Socket Firewall mode: \`$mode\`"
114
+ echo "- author_association: \`$AUTHOR_ASSOC\`"
115
+ echo "- dependabot: \`$IS_DEPENDABOT\` | fork: \`$IS_FORK\`"
116
+ } >> "$GITHUB_STEP_SUMMARY"
117
+
118
+ - name: Summarize review expectations
119
+ env:
120
+ PR_URL: ${{ github.event.pull_request.html_url }}
121
+ run: |
122
+ {
123
+ echo "## Dependency Review Checklist"
124
+ echo "- PR: $PR_URL"
125
+ echo "- Confirm upstream release notes before merge"
126
+ echo "- Do not treat a dependency PR as trusted solely because of the actor"
127
+ echo "- This workflow runs in pull_request context only; no publish secrets are exposed"
128
+ } >> "$GITHUB_STEP_SUMMARY"
129
+
130
+ python-sfw-smoke-free:
131
+ needs: inspect
132
+ if: |
133
+ needs.inspect.outputs.python_deps_changed == 'true' &&
134
+ needs.inspect.outputs.sfw_mode == 'firewall-free'
135
+ runs-on: ubuntu-latest
136
+ timeout-minutes: 15
137
+ steps:
138
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
139
+ with:
140
+ fetch-depth: 1
141
+ persist-credentials: false
142
+
143
+ - name: Prepare SFW artifact directory
144
+ run: |
145
+ mkdir -p sfw-artifacts
146
+ {
147
+ echo "mode=firewall-free"
148
+ echo "manifest=python"
149
+ echo "pr=${{ github.event.pull_request.number }}"
150
+ echo "sha=${{ github.event.pull_request.head.sha }}"
151
+ } > sfw-artifacts/context.txt
152
+
153
+ - uses: ./.github/actions/setup-sfw
154
+ with:
155
+ uv: "true"
156
+ mode: firewall-free
157
+
158
+ - name: Sync project through Socket Firewall
159
+ # `sfw uv sync` is the intended way to route uv through Socket Firewall
160
+ # (per Socket's own uv wrapper guidance). --locked verifies the exact
161
+ # uv.lock set and fails on lockfile drift rather than silently
162
+ # re-resolving, so the firewall inspects precisely what would install.
163
+ # Note: uv's sfw integration is quieter than npm/pip -- it does not
164
+ # print the "N packages fetched" footer, but interception is active.
165
+ #
166
+ # Use the runner's setup-python interpreter and forbid managed-Python
167
+ # downloads. The firewall is here to vet PyPI installs, not the
168
+ # interpreter/toolchain download path.
169
+ #
170
+ # pipefail keeps sfw's exit code through the tee so a firewall block
171
+ # still fails the job; tee captures the report for the artifact upload.
172
+ env:
173
+ UV_PYTHON: "3.12"
174
+ UV_PYTHON_DOWNLOADS: never
175
+ run: |
176
+ set -o pipefail
177
+ sfw uv sync --locked --extra test --extra dev 2>&1 | tee sfw-artifacts/sfw-uv-sync.log
178
+
179
+ - name: Import smoke test
180
+ env:
181
+ UV_PYTHON: "3.12"
182
+ UV_PYTHON_DOWNLOADS: never
183
+ run: |
184
+ set -o pipefail
185
+ uv run python -c "
186
+ from socketsecurity.socketcli import cli, build_socket_sdk
187
+ from socketsecurity.core import Core
188
+ from socketsecurity.core.exceptions import (
189
+ APIFailure, RequestTimeoutExceeded, APIResourceNotFound,
190
+ )
191
+ from socketsecurity.core.git_interface import Git
192
+ from socketsecurity.config import CliConfig
193
+ print('import smoke OK')
194
+ " 2>&1 | tee sfw-artifacts/import-smoke.log
195
+
196
+ - name: Collect SFW JSON report
197
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
198
+ # file) and reads it back in its post step to render the job summary, so
199
+ # COPY (don't move) the report into the bundle. sfw writes it even when
200
+ # it blocks an install -- always() keeps it on failures too.
201
+ if: always()
202
+ run: |
203
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
204
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
205
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
206
+ else
207
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
208
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
209
+ fi
210
+
211
+ - name: Upload SFW report artifact
212
+ if: always()
213
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
214
+ with:
215
+ name: socket-firewall-free-python-${{ github.event.pull_request.number }}
216
+ path: sfw-artifacts/
217
+ if-no-files-found: warn
218
+ retention-days: 14
219
+
220
+ python-sfw-smoke-enterprise:
221
+ needs: inspect
222
+ if: |
223
+ needs.inspect.outputs.python_deps_changed == 'true' &&
224
+ needs.inspect.outputs.sfw_mode == 'firewall-enterprise'
225
+ runs-on: ubuntu-latest
226
+ timeout-minutes: 15
227
+ environment: socket-firewall
228
+ steps:
229
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
230
+ with:
231
+ fetch-depth: 1
232
+ persist-credentials: false
233
+
234
+ - name: Prepare SFW artifact directory
235
+ run: |
236
+ mkdir -p sfw-artifacts
237
+ {
238
+ echo "mode=firewall-enterprise"
239
+ echo "manifest=python"
240
+ echo "pr=${{ github.event.pull_request.number }}"
241
+ echo "sha=${{ github.event.pull_request.head.sha }}"
242
+ } > sfw-artifacts/context.txt
243
+
244
+ - uses: ./.github/actions/setup-sfw
245
+ with:
246
+ uv: "true"
247
+ mode: firewall-enterprise
248
+ socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }}
249
+
250
+ - name: Sync project through Socket Firewall
251
+ # `sfw uv sync` is the intended way to route uv through Socket Firewall
252
+ # (per Socket's own uv wrapper guidance). --locked verifies the exact
253
+ # uv.lock set and fails on lockfile drift rather than silently
254
+ # re-resolving, so the firewall inspects precisely what would install.
255
+ # Note: uv's sfw integration is quieter than npm/pip -- it does not
256
+ # print the "N packages fetched" footer, but interception is active.
257
+ #
258
+ # Use the runner's setup-python interpreter and forbid managed-Python
259
+ # downloads. The firewall is here to vet PyPI installs, not the
260
+ # interpreter/toolchain download path.
261
+ #
262
+ # pipefail keeps sfw's exit code through the tee so a firewall block
263
+ # still fails the job; tee captures the report for the artifact upload.
264
+ env:
265
+ UV_PYTHON: "3.12"
266
+ UV_PYTHON_DOWNLOADS: never
267
+ run: |
268
+ set -o pipefail
269
+ sfw uv sync --locked --extra test --extra dev 2>&1 | tee sfw-artifacts/sfw-uv-sync.log
270
+
271
+ - name: Import smoke test
272
+ env:
273
+ UV_PYTHON: "3.12"
274
+ UV_PYTHON_DOWNLOADS: never
275
+ run: |
276
+ set -o pipefail
277
+ uv run python -c "
278
+ from socketsecurity.socketcli import cli, build_socket_sdk
279
+ from socketsecurity.core import Core
280
+ from socketsecurity.core.exceptions import (
281
+ APIFailure, RequestTimeoutExceeded, APIResourceNotFound,
282
+ )
283
+ from socketsecurity.core.git_interface import Git
284
+ from socketsecurity.config import CliConfig
285
+ print('import smoke OK')
286
+ " 2>&1 | tee sfw-artifacts/import-smoke.log
287
+
288
+ - name: Collect SFW JSON report
289
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
290
+ # file) and reads it back in its post step to render the job summary, so
291
+ # COPY (don't move) the report into the bundle. sfw writes it even when
292
+ # it blocks an install -- always() keeps it on failures too.
293
+ if: always()
294
+ run: |
295
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
296
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
297
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
298
+ else
299
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
300
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
301
+ fi
302
+
303
+ - name: Upload SFW report artifact
304
+ if: always()
305
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
306
+ with:
307
+ name: socket-firewall-enterprise-python-${{ github.event.pull_request.number }}
308
+ path: sfw-artifacts/
309
+ if-no-files-found: warn
310
+ retention-days: 14
311
+
312
+ fixture-npm-sfw-smoke-free:
313
+ needs: inspect
314
+ if: |
315
+ needs.inspect.outputs.fixture_npm_changed == 'true' &&
316
+ needs.inspect.outputs.sfw_mode == 'firewall-free'
317
+ runs-on: ubuntu-latest
318
+ timeout-minutes: 15
319
+ steps:
320
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
321
+ with:
322
+ fetch-depth: 1
323
+ persist-credentials: false
324
+
325
+ - name: Prepare SFW artifact directory
326
+ run: |
327
+ mkdir -p sfw-artifacts
328
+ {
329
+ echo "mode=firewall-free"
330
+ echo "manifest=npm"
331
+ echo "pr=${{ github.event.pull_request.number }}"
332
+ echo "sha=${{ github.event.pull_request.head.sha }}"
333
+ } > sfw-artifacts/context.txt
334
+
335
+ - uses: ./.github/actions/setup-sfw
336
+ with:
337
+ node: "true"
338
+ mode: firewall-free
339
+
340
+ - name: Install fixture through Socket Firewall
341
+ working-directory: tests/e2e/fixtures/simple-npm
342
+ # Tee to an absolute path under the workspace so the log lands in the
343
+ # repo-root sfw-artifacts/ dir despite this step's working-directory.
344
+ run: |
345
+ set -o pipefail
346
+ sfw npm install --no-audit --no-fund --ignore-scripts 2>&1 | tee "$GITHUB_WORKSPACE/sfw-artifacts/sfw-npm-install.log"
347
+
348
+ - name: Collect SFW JSON report
349
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
350
+ # file) and reads it back in its post step to render the job summary, so
351
+ # COPY (don't move) the report into the bundle. sfw writes it even when
352
+ # it blocks an install -- always() keeps it on failures too.
353
+ if: always()
354
+ run: |
355
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
356
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
357
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
358
+ else
359
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
360
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
361
+ fi
362
+
363
+ - name: Upload SFW report artifact
364
+ if: always()
365
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
366
+ with:
367
+ name: socket-firewall-free-npm-${{ github.event.pull_request.number }}
368
+ path: sfw-artifacts/
369
+ if-no-files-found: warn
370
+ retention-days: 14
371
+
372
+ fixture-npm-sfw-smoke-enterprise:
373
+ needs: inspect
374
+ if: |
375
+ needs.inspect.outputs.fixture_npm_changed == 'true' &&
376
+ needs.inspect.outputs.sfw_mode == 'firewall-enterprise'
377
+ runs-on: ubuntu-latest
378
+ timeout-minutes: 15
379
+ environment: socket-firewall
380
+ steps:
381
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
382
+ with:
383
+ fetch-depth: 1
384
+ persist-credentials: false
385
+
386
+ - name: Prepare SFW artifact directory
387
+ run: |
388
+ mkdir -p sfw-artifacts
389
+ {
390
+ echo "mode=firewall-enterprise"
391
+ echo "manifest=npm"
392
+ echo "pr=${{ github.event.pull_request.number }}"
393
+ echo "sha=${{ github.event.pull_request.head.sha }}"
394
+ } > sfw-artifacts/context.txt
395
+
396
+ - uses: ./.github/actions/setup-sfw
397
+ with:
398
+ node: "true"
399
+ mode: firewall-enterprise
400
+ socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }}
401
+
402
+ - name: Install fixture through Socket Firewall
403
+ working-directory: tests/e2e/fixtures/simple-npm
404
+ # Tee to an absolute path under the workspace so the log lands in the
405
+ # repo-root sfw-artifacts/ dir despite this step's working-directory.
406
+ run: |
407
+ set -o pipefail
408
+ sfw npm install --no-audit --no-fund --ignore-scripts 2>&1 | tee "$GITHUB_WORKSPACE/sfw-artifacts/sfw-npm-install.log"
409
+
410
+ - name: Collect SFW JSON report
411
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
412
+ # file) and reads it back in its post step to render the job summary, so
413
+ # COPY (don't move) the report into the bundle. sfw writes it even when
414
+ # it blocks an install -- always() keeps it on failures too.
415
+ if: always()
416
+ run: |
417
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
418
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
419
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
420
+ else
421
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
422
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
423
+ fi
424
+
425
+ - name: Upload SFW report artifact
426
+ if: always()
427
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
428
+ with:
429
+ name: socket-firewall-enterprise-npm-${{ github.event.pull_request.number }}
430
+ path: sfw-artifacts/
431
+ if-no-files-found: warn
432
+ retention-days: 14
433
+
434
+ fixture-pypi-sfw-smoke-free:
435
+ needs: inspect
436
+ if: |
437
+ needs.inspect.outputs.fixture_pypi_changed == 'true' &&
438
+ needs.inspect.outputs.sfw_mode == 'firewall-free'
439
+ runs-on: ubuntu-latest
440
+ timeout-minutes: 15
441
+ steps:
442
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
443
+ with:
444
+ fetch-depth: 1
445
+ persist-credentials: false
446
+
447
+ - name: Prepare SFW artifact directory
448
+ run: |
449
+ mkdir -p sfw-artifacts
450
+ {
451
+ echo "mode=firewall-free"
452
+ echo "manifest=pypi"
453
+ echo "pr=${{ github.event.pull_request.number }}"
454
+ echo "sha=${{ github.event.pull_request.head.sha }}"
455
+ } > sfw-artifacts/context.txt
456
+
457
+ - uses: ./.github/actions/setup-sfw
458
+ with:
459
+ python: "true"
460
+ mode: firewall-free
461
+
462
+ - name: Install fixture through Socket Firewall
463
+ working-directory: tests/e2e/fixtures/simple-pypi
464
+ # Tee to an absolute path under the workspace so the log lands in the
465
+ # repo-root sfw-artifacts/ dir despite this step's working-directory.
466
+ run: |
467
+ set -o pipefail
468
+ python -m venv .venv
469
+ # shellcheck disable=SC1091
470
+ source .venv/bin/activate
471
+ sfw pip install -r requirements.txt 2>&1 | tee "$GITHUB_WORKSPACE/sfw-artifacts/sfw-pip-install.log"
472
+
473
+ - name: Collect SFW JSON report
474
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
475
+ # file) and reads it back in its post step to render the job summary, so
476
+ # COPY (don't move) the report into the bundle. sfw writes it even when
477
+ # it blocks an install -- always() keeps it on failures too.
478
+ if: always()
479
+ run: |
480
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
481
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
482
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
483
+ else
484
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
485
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
486
+ fi
487
+
488
+ - name: Upload SFW report artifact
489
+ if: always()
490
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
491
+ with:
492
+ name: socket-firewall-free-pypi-${{ github.event.pull_request.number }}
493
+ path: sfw-artifacts/
494
+ if-no-files-found: warn
495
+ retention-days: 14
496
+
497
+ fixture-pypi-sfw-smoke-enterprise:
498
+ needs: inspect
499
+ if: |
500
+ needs.inspect.outputs.fixture_pypi_changed == 'true' &&
501
+ needs.inspect.outputs.sfw_mode == 'firewall-enterprise'
502
+ runs-on: ubuntu-latest
503
+ timeout-minutes: 15
504
+ environment: socket-firewall
505
+ steps:
506
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
507
+ with:
508
+ fetch-depth: 1
509
+ persist-credentials: false
510
+
511
+ - name: Prepare SFW artifact directory
512
+ run: |
513
+ mkdir -p sfw-artifacts
514
+ {
515
+ echo "mode=firewall-enterprise"
516
+ echo "manifest=pypi"
517
+ echo "pr=${{ github.event.pull_request.number }}"
518
+ echo "sha=${{ github.event.pull_request.head.sha }}"
519
+ } > sfw-artifacts/context.txt
520
+
521
+ - uses: ./.github/actions/setup-sfw
522
+ with:
523
+ python: "true"
524
+ mode: firewall-enterprise
525
+ socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }}
526
+
527
+ - name: Install fixture through Socket Firewall
528
+ working-directory: tests/e2e/fixtures/simple-pypi
529
+ # Tee to an absolute path under the workspace so the log lands in the
530
+ # repo-root sfw-artifacts/ dir despite this step's working-directory.
531
+ run: |
532
+ set -o pipefail
533
+ python -m venv .venv
534
+ # shellcheck disable=SC1091
535
+ source .venv/bin/activate
536
+ sfw pip install -r requirements.txt 2>&1 | tee "$GITHUB_WORKSPACE/sfw-artifacts/sfw-pip-install.log"
537
+
538
+ - name: Collect SFW JSON report
539
+ # socketdev/action points sfw at SFW_JSON_REPORT_PATH (a $RUNNER_TEMP
540
+ # file) and reads it back in its post step to render the job summary, so
541
+ # COPY (don't move) the report into the bundle. sfw writes it even when
542
+ # it blocks an install -- always() keeps it on failures too.
543
+ if: always()
544
+ run: |
545
+ if [ -n "${SFW_JSON_REPORT_PATH:-}" ] && [ -f "$SFW_JSON_REPORT_PATH" ]; then
546
+ cp "$SFW_JSON_REPORT_PATH" "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report.json"
547
+ echo "Collected SFW report -> sfw-artifacts/sfw-report.json"
548
+ else
549
+ echo "No SFW JSON report found at '${SFW_JSON_REPORT_PATH:-<unset>}'." \
550
+ > "$GITHUB_WORKSPACE/sfw-artifacts/sfw-report-missing.txt"
551
+ fi
552
+
553
+ - name: Upload SFW report artifact
554
+ if: always()
555
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
556
+ with:
557
+ name: socket-firewall-enterprise-pypi-${{ github.event.pull_request.number }}
558
+ path: sfw-artifacts/
559
+ if-no-files-found: warn
560
+ retention-days: 14
561
+
562
+ dockerfile-smoke:
563
+ needs: inspect
564
+ if: needs.inspect.outputs.dockerfile_changed == 'true'
565
+ runs-on: ubuntu-latest
566
+ timeout-minutes: 20
567
+ steps:
568
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
569
+ with:
570
+ fetch-depth: 1
571
+ persist-credentials: false
572
+
573
+ - name: Build the Dockerfile (no push)
574
+ run: docker build --pull -t socket-python-cli:dependabot-smoke .
575
+
576
+ workflow-notice:
577
+ needs: inspect
578
+ if: needs.inspect.outputs.workflow_or_action_changed == 'true'
579
+ runs-on: ubuntu-latest
580
+ timeout-minutes: 2
581
+ steps:
582
+ - name: Flag workflow-sensitive updates
583
+ run: |
584
+ {
585
+ echo "## Sensitive File Notice"
586
+ echo "This PR changes workflow, composite-action, or dependabot config files."
587
+ echo "Require explicit human review before merge."
588
+ } >> "$GITHUB_STEP_SUMMARY"