From e1bcae99bbb36bef4a765314a0f2c0ec280cb641 Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:04:03 +0100 Subject: [PATCH 1/8] Run Snyk Open Source monitor even when Snyk Code test fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither step had an if: condition, so a Code (SAST) finding failing its step silently skipped Open Source (SCA) dependency monitoring too — confirmed on https://github.com/AviBackToBlack/lidaldi/actions/runs/30214194004/job/89825422862, where `Snyk Open Source monitor` shows skipped. The two scans are independent concerns; one failing shouldn't blind the other. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 33d769e..3645560 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -73,7 +73,10 @@ jobs: run: snyk code test --sarif > snyk-code.sarif # || true # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. + # Independent of Snyk Code above — a Code finding failing that step + # shouldn't stop dependency monitoring from running and reporting. - name: Snyk Open Source monitor + if: always() run: snyk monitor --all-projects # `snyk code test` above still fails the job on real findings (the From aae34e6b2f65a470d4d345e90ecfa237eb30c29a Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:10:40 +0100 Subject: [PATCH 2/8] Install Python deps too, so snyk monitor covers the whole repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the Open Source monitor step actually runs (previous commit), it surfaced a second issue: `snyk monitor --all-projects` reported "Missing required packages" for requirements.txt and tests/requirements.txt — this workflow only ever set up Node, so the two pip-based projects had nothing installed to resolve against. Added actions/setup-python + pip install, mirroring what's already done for the frontend's npm dependencies. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 3645560..9ab8a9d 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -52,8 +52,11 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - # Snyk Open Source resolves frontend/'s dependency tree via its - # node_modules, so it needs to be installed first. + # Snyk Open Source resolves each project's dependency tree from an + # actual installed environment, not just the manifest — so both the + # Python (root + tests/) and Node (frontend/) dependencies need to + # be installed first, or `snyk monitor` reports "Missing required + # packages" for whichever ecosystem wasn't set up. - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -62,6 +65,13 @@ jobs: working-directory: frontend run: npm ci + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: "3.12" + - name: Install Python dependencies + run: pip install -r requirements.txt -r tests/requirements.txt + - name: Set up Snyk CLI to check for security issues # Snyk can be used to break the build when it detects security issues. # In this case we want to upload the SAST issues to GitHub Code Scanning From 46e89538f9c0672ba5973714982be93baa8d84be Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:13:31 +0100 Subject: [PATCH 3/8] Use success()||failure() instead of always() to respect cancellation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Devin Review on this PR: always() also runs a step when the job is cancelled, and cancel-in-progress cancels superseded PR runs — so a cancelled run could still push a snyk monitor snapshot (a persistent record) or attempt a SARIF upload. success()||failure() runs regardless of the Code test's pass/fail outcome (the actual intent) while still skipping on cancellation. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 9ab8a9d..fdefef8 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -85,8 +85,12 @@ jobs: # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. # Independent of Snyk Code above — a Code finding failing that step # shouldn't stop dependency monitoring from running and reporting. + # success() || failure(), not always(): always() also fires on job + # cancellation, and cancel-in-progress above cancels superseded PR + # runs — we don't want a cancelled run still pushing a `snyk + # monitor` snapshot (a persistent record, unlike `snyk test`). - name: Snyk Open Source monitor - if: always() + if: success() || failure() run: snyk monitor --all-projects # `snyk code test` above still fails the job on real findings (the @@ -96,7 +100,7 @@ jobs: # file actually contains parseable SARIF. - name: Check SARIF output is valid id: sarif-check - if: always() + if: success() || failure() run: | if [ -s snyk-code.sarif ] && jq empty snyk-code.sarif >/dev/null 2>&1; then echo "valid=true" >> "$GITHUB_OUTPUT" @@ -106,7 +110,7 @@ jobs: # Push the Snyk Code results into GitHub Code Scanning tab. - name: Upload result to GitHub Code Scanning - if: always() && steps.sarif-check.outputs.valid == 'true' + if: (success() || failure()) && steps.sarif-check.outputs.valid == 'true' uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: sarif_file: snyk-code.sarif From 2b28aedebd5b64999b42a31a857279630ef71c86 Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:23:20 +0100 Subject: [PATCH 4/8] Use --sarif-file-output so the job log shows actual findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `snyk code test --sarif > snyk-code.sarif` redirects ALL output into the SARIF file, so a failing run's log shows nothing but "Process completed with exit code 1" — no indication it's real findings vs. a broken pipeline. --sarif-file-output writes the SARIF to disk while still printing the normal human-readable findings list (file, line, rule, severity) to the console. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index fdefef8..478673c 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -79,8 +79,14 @@ jobs: # Runs Snyk Code (SAST) analysis and uploads result into GitHub. # Use || true to not fail the pipeline + # + # --sarif-file-output (not `--sarif > file`): the latter redirects + # ALL output into the SARIF file, so a failed run's log shows only + # "Process completed with exit code 1" and nothing about why — + # --sarif-file-output writes the SARIF to disk while still printing + # the normal human-readable findings list to the job log. - name: Snyk Code test - run: snyk code test --sarif > snyk-code.sarif # || true + run: snyk code test --sarif-file-output=snyk-code.sarif # || true # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. # Independent of Snyk Code above — a Code finding failing that step From 5973c1992637a988660f6fd1384c34a93444751a Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:31:20 +0100 Subject: [PATCH 5/8] TEMP: add -d debug flag to chase the trailing 403 Forbidden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnostic only — will be reverted once the failing call is identified. Not for merge as-is. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 478673c..11fbddc 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -86,7 +86,7 @@ jobs: # --sarif-file-output writes the SARIF to disk while still printing # the normal human-readable findings list to the job log. - name: Snyk Code test - run: snyk code test --sarif-file-output=snyk-code.sarif # || true + run: snyk code test --sarif-file-output=snyk-code.sarif -d # || true # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. # Independent of Snyk Code above — a Code finding failing that step From dabe8fb7fad0c401603e60e75e0085e651268136 Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:33:53 +0100 Subject: [PATCH 6/8] Remove diagnostic -d flag; document the benign 403 it explained MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Traced the trailing "403 Forbidden (SNYK-CLI-0000)" via -d debug output: it's a single failed GET /rest/orgs/{id} call, where the CLI tries to resolve the org ID into a display slug. The actual scan (POST .../tests, result polling, findings retrieval) all succeed regardless — this is a PAT-scope gap on a non-essential lookup, not a workflow bug. Documented inline instead of leaving debug logging on permanently. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 11fbddc..63ed113 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -85,8 +85,17 @@ jobs: # "Process completed with exit code 1" and nothing about why — # --sarif-file-output writes the SARIF to disk while still printing # the normal human-readable findings list to the job log. + # + # Known-benign trailing error: the log may also show a 403 on + # GET /rest/orgs/{id} ("Failed to determine slugname..."). That's + # the CLI trying to resolve the org ID into a display name/slug — + # unrelated to the actual scan, which completes and reports findings + # correctly regardless. It 403s because this PAT wasn't granted a + # scope covering that org-details endpoint; see + # https://app.snyk.io/account/personal-access-tokens to fix at the + # source, or ignore it (cosmetic only). - name: Snyk Code test - run: snyk code test --sarif-file-output=snyk-code.sarif -d # || true + run: snyk code test --sarif-file-output=snyk-code.sarif # || true # Runs Snyk Open Source (SCA) analysis and uploads result to Snyk. # Independent of Snyk Code above — a Code finding failing that step From 36ccdfed8fe22b3fd533cba0472a399f5836dfa7 Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:38:15 +0100 Subject: [PATCH 7/8] Try org slug instead of org ID to sidestep the /rest/orgs/{id} 403 The CLI's only failing call was resolving SNYK_CFG_ORG's UUID into a display slug via GET /rest/orgs/{id}, which 403s for this PAT's scopes. Passing the slug (avibacktoblack, taken from the snyk monitor snapshot URLs) directly should make that lookup unnecessary. Verifying with this push before finalizing. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 63ed113..0dbbd56 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -48,7 +48,10 @@ jobs: # A personal access token (unlike a legacy per-account API key) isn't # tied to a single default org, so the CLI can't infer which org to # act against without this — omitting it 403s with an empty org name. - SNYK_CFG_ORG: "08ee9b45-66fd-4689-b808-50fc0e58ce15" + # Using the org slug (not the org ID/UUID) avoids a separate CLI call + # to resolve ID -> slug (GET /rest/orgs/{id}), which 403s for this + # PAT and otherwise shows up as a spurious error in the job log. + SNYK_CFG_ORG: "avibacktoblack" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -85,15 +88,6 @@ jobs: # "Process completed with exit code 1" and nothing about why — # --sarif-file-output writes the SARIF to disk while still printing # the normal human-readable findings list to the job log. - # - # Known-benign trailing error: the log may also show a 403 on - # GET /rest/orgs/{id} ("Failed to determine slugname..."). That's - # the CLI trying to resolve the org ID into a display name/slug — - # unrelated to the actual scan, which completes and reports findings - # correctly regardless. It 403s because this PAT wasn't granted a - # scope covering that org-details endpoint; see - # https://app.snyk.io/account/personal-access-tokens to fix at the - # source, or ignore it (cosmetic only). - name: Snyk Code test run: snyk code test --sarif-file-output=snyk-code.sarif # || true From 83b0c037d10ea7411d7ffe4cde8e3b728ba889c8 Mon Sep 17 00:00:00 2001 From: AviBackToBlack <54722547+AviBackToBlack@users.noreply.github.com> Date: Mon, 27 Jul 2026 00:40:38 +0100 Subject: [PATCH 8/8] Revert org slug -> org ID; slug didn't avoid the 403 (confirmed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested passing the org slug instead of the UUID to see if it'd skip the GET /rest/orgs/{id} resolution call that 403s — it doesn't; the CLI hits that endpoint regardless of which identifier format it's given. So this is confirmed to be a PAT scope gap, not an identifier format issue. Reverting to the UUID (more stable than a slug, which can be renamed) and documenting the actual fix path inline. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/snyk-security.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/snyk-security.yml b/.github/workflows/snyk-security.yml index 0dbbd56..2c030a7 100644 --- a/.github/workflows/snyk-security.yml +++ b/.github/workflows/snyk-security.yml @@ -48,10 +48,14 @@ jobs: # A personal access token (unlike a legacy per-account API key) isn't # tied to a single default org, so the CLI can't infer which org to # act against without this — omitting it 403s with an empty org name. - # Using the org slug (not the org ID/UUID) avoids a separate CLI call - # to resolve ID -> slug (GET /rest/orgs/{id}), which 403s for this - # PAT and otherwise shows up as a spurious error in the job log. - SNYK_CFG_ORG: "avibacktoblack" + # Note: the CLI still calls GET /rest/orgs/{id} regardless of whether + # this is the org ID or its slug (tried both — same 403 either way), + # to resolve/validate the org. That call 403s because the PAT wasn't + # granted a scope covering it; it's cosmetic — the actual scan/monitor + # both succeed regardless. Fix at the source by granting that scope + # (or regenerating the PAT with it) at + # https://app.snyk.io/account/personal-access-tokens. + SNYK_CFG_ORG: "08ee9b45-66fd-4689-b808-50fc0e58ce15" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1