@@ -18,7 +18,12 @@ name: dependency-review
1818# Only Enterprise jobs declare the socket-firewall environment. Free jobs do
1919# not touch that environment or its token.
2020#
21- # Pattern adapted from SocketDev/socket-basics.
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.
2227
2328on :
2429 pull_request :
@@ -135,6 +140,16 @@ jobs:
135140 fetch-depth : 1
136141 persist-credentials : false
137142
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+
138153 - uses : ./.github/actions/setup-sfw
139154 with :
140155 uv : " true"
@@ -151,16 +166,22 @@ jobs:
151166 # Use the runner's setup-python interpreter and forbid managed-Python
152167 # downloads. The firewall is here to vet PyPI installs, not the
153168 # 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.
154172 env :
155173 UV_PYTHON : " 3.12"
156174 UV_PYTHON_DOWNLOADS : never
157- run : sfw uv sync --locked --extra test --extra dev
175+ run : |
176+ set -o pipefail
177+ sfw uv sync --locked --extra test --extra dev 2>&1 | tee sfw-artifacts/sfw-uv-sync.log
158178
159179 - name : Import smoke test
160180 env :
161181 UV_PYTHON : " 3.12"
162182 UV_PYTHON_DOWNLOADS : never
163183 run : |
184+ set -o pipefail
164185 uv run python -c "
165186 from socketsecurity.socketcli import cli, build_socket_sdk
166187 from socketsecurity.core import Core
@@ -170,7 +191,31 @@ jobs:
170191 from socketsecurity.core.git_interface import Git
171192 from socketsecurity.config import CliConfig
172193 print('import smoke OK')
173- "
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
174219
175220 python-sfw-smoke-enterprise :
176221 needs : inspect
@@ -186,6 +231,16 @@ jobs:
186231 fetch-depth : 1
187232 persist-credentials : false
188233
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+
189244 - uses : ./.github/actions/setup-sfw
190245 with :
191246 uv : " true"
@@ -203,16 +258,22 @@ jobs:
203258 # Use the runner's setup-python interpreter and forbid managed-Python
204259 # downloads. The firewall is here to vet PyPI installs, not the
205260 # 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.
206264 env :
207265 UV_PYTHON : " 3.12"
208266 UV_PYTHON_DOWNLOADS : never
209- run : sfw uv sync --locked --extra test --extra dev
267+ run : |
268+ set -o pipefail
269+ sfw uv sync --locked --extra test --extra dev 2>&1 | tee sfw-artifacts/sfw-uv-sync.log
210270
211271 - name : Import smoke test
212272 env :
213273 UV_PYTHON : " 3.12"
214274 UV_PYTHON_DOWNLOADS : never
215275 run : |
276+ set -o pipefail
216277 uv run python -c "
217278 from socketsecurity.socketcli import cli, build_socket_sdk
218279 from socketsecurity.core import Core
@@ -222,7 +283,31 @@ jobs:
222283 from socketsecurity.core.git_interface import Git
223284 from socketsecurity.config import CliConfig
224285 print('import smoke OK')
225- "
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
226311
227312 fixture-npm-sfw-smoke-free :
228313 needs : inspect
@@ -237,14 +322,52 @@ jobs:
237322 fetch-depth : 1
238323 persist-credentials : false
239324
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+
240335 - uses : ./.github/actions/setup-sfw
241336 with :
242337 node : " true"
243338 mode : firewall-free
244339
245340 - name : Install fixture through Socket Firewall
246341 working-directory : tests/e2e/fixtures/simple-npm
247- run : sfw npm install --no-audit --no-fund --ignore-scripts
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
248371
249372 fixture-npm-sfw-smoke-enterprise :
250373 needs : inspect
@@ -260,6 +383,16 @@ jobs:
260383 fetch-depth : 1
261384 persist-credentials : false
262385
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+
263396 - uses : ./.github/actions/setup-sfw
264397 with :
265398 node : " true"
@@ -268,7 +401,35 @@ jobs:
268401
269402 - name : Install fixture through Socket Firewall
270403 working-directory : tests/e2e/fixtures/simple-npm
271- run : sfw npm install --no-audit --no-fund --ignore-scripts
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
272433
273434 fixture-pypi-sfw-smoke-free :
274435 needs : inspect
@@ -283,18 +444,55 @@ jobs:
283444 fetch-depth : 1
284445 persist-credentials : false
285446
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+
286457 - uses : ./.github/actions/setup-sfw
287458 with :
288459 python : " true"
289460 mode : firewall-free
290461
291462 - name : Install fixture through Socket Firewall
292463 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.
293466 run : |
467+ set -o pipefail
294468 python -m venv .venv
295469 # shellcheck disable=SC1091
296470 source .venv/bin/activate
297- sfw pip install -r requirements.txt
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
298496
299497 fixture-pypi-sfw-smoke-enterprise :
300498 needs : inspect
@@ -310,6 +508,16 @@ jobs:
310508 fetch-depth : 1
311509 persist-credentials : false
312510
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+
313521 - uses : ./.github/actions/setup-sfw
314522 with :
315523 python : " true"
@@ -318,11 +526,38 @@ jobs:
318526
319527 - name : Install fixture through Socket Firewall
320528 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.
321531 run : |
532+ set -o pipefail
322533 python -m venv .venv
323534 # shellcheck disable=SC1091
324535 source .venv/bin/activate
325- sfw pip install -r requirements.txt
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
326561
327562 dockerfile-smoke :
328563 needs : inspect
0 commit comments