Repository navigation
fix(ci): npm audit advisories and scanner decoupling in Security Scanning - #1716
Conversation
brace-expansion (GHSA-rgw5-rvv9-x895) and fast-uri (GHSA-7p8r-x3mc-p8w7) were flagged as high severity by npm audit, failing Security Scanning on every PR regardless of frontend changes. Both are transitive dependencies; npm audit fix bumps only the lockfile entries (brace-expansion 5.0.8 -> 5.0.9, fast-uri 3.1.4 -> 3.1.5) with no change to package.json's direct dependency ranges. npm audit --audit-level=high now exits 0, and the frontend build and test suite are unaffected by the bump.
The Security Scanning job ran govulncheck, npm audit, gosec, and two Trivy scans as sequential steps in one job. GitHub Actions skips every step after a failing one by default, so a single frontend npm advisory silently disabled Go SAST (gosec) and Terraform IaC scanning (Trivy config) repo-wide, while the resulting failure pointed at a missing SARIF upload rather than the actual cause. Give govulncheck, npm audit, gosec, and both Trivy scans `if: always()` so each one runs independent of whether an earlier scanner passed. The job still fails overall if any scanner step fails, since always() does not suppress a step's own failure. Also tighten the SARIF upload steps for gosec and both Trivy scans: each now checks `steps.<scanner>.outcome != 'skipped'` before uploading, so a genuinely unrun scanner (skipped upstream of the scanner steps themselves) is tolerated without a confusing "Path does not exist" failure, while a scanner that ran and left no file still fails loud.
📝 WalkthroughWalkthroughThe CI workflow runs security scanners independently after checkout and Go setup. Gosec completes all module scans, merges available SARIF output, and preserves failure status. SARIF uploads use scanner outcomes. Gosec now uses v2.28.0. ChangesSecurity scanner workflow
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Checkout
participant GoSetup
participant SecurityScanners
participant SARIFUpload
Checkout->>GoSetup: complete Go setup
GoSetup->>SecurityScanners: run scanners independently
SecurityScanners->>SecurityScanners: scan modules and collect outcomes
SecurityScanners->>SARIFUpload: provide available SARIF results
SARIFUpload-->>SecurityScanners: report upload status
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)
302-307: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winStop scanner work when the workflow is cancelled.
always()remains true after cancellation. These steps can continue tool installation, scanning, and SARIF uploads after a user cancels the run. GitHub recommendsif: ${{ !cancelled() }}when a step must run after success or failure but must stop on cancellation. (docs.github.com)Use
!cancelled()for scanner steps and combine it with the existing outcome check for upload steps.Suggested condition pattern
- if: always() + if: ${{ !cancelled() }} - if: always() && steps.gosec.outcome != 'skipped' + if: ${{ !cancelled() && steps.gosec.outcome != 'skipped' }}Also applies to: 324-326, 334-336, 364-369, 375-378, 395-396, 411-414, 427-428
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 302 - 307, Replace the scanner-step conditions using always() with !cancelled() so tool installation and scans stop when the workflow is cancelled while still running after failures. For SARIF upload steps, combine !cancelled() with their existing outcome check, preserving the current upload behavior for completed scanner steps. Apply this consistently to all referenced scanner and upload steps.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 332-336: Align the gosec version used by the pre-commit workflow
with the v2.28.0 version used in CI, updating its corresponding cache key;
alternatively, revise the documented version contract consistently if the older
pin must remain. Keep the existing GO_VERSION compatibility and scanner behavior
unchanged.
- Around line 364-369: The gosec scan step currently exits on the first failed
module, preventing SARIF merging. Update the gosec module loop and completion
flow to record a failure status, continue scanning every module, merge all
generated SARIF files, and exit with the recorded failure status only after the
merge completes.
- Around line 364-369: Update the scanner jobs around the checkout, setup-go,
gosec, and matching SARIF upload steps to assign IDs to checkout and setup-go,
then require both prerequisite step outcomes to be success in each scanner
condition and its corresponding upload condition. Preserve the existing
fail-loud behavior when prerequisites succeed but the scanner does not produce
SARIF, while preventing scanners and uploads from running after prerequisite
failure.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 302-307: Replace the scanner-step conditions using always() with
!cancelled() so tool installation and scans stop when the workflow is cancelled
while still running after failures. For SARIF upload steps, combine !cancelled()
with their existing outcome check, preserving the current upload behavior for
completed scanner steps. Apply this consistently to all referenced scanner and
upload steps.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f0554ea2-73e0-461e-97aa-e9f90468db2b
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (1)
.github/workflows/ci.yml
…a success Two related CodeRabbit findings on PR #1716, both in the Security Scanning job: Closes #1717. The gosec step's per-module loop ran under `set -e`, and gosec itself exits non-zero both for a real finding and for a processing error. The first module with a finding aborted the loop before the later modules were scanned and before the final SARIF merge ran, so the upload step failed on a missing gosec-results.sarif instead of surfacing the actual finding -- the same failure class this PR's first commit fixed for cross-scanner coupling, one level in. Guard the gosec invocation in `if ! ( ... )` so a non-zero exit is recorded in a `status` variable instead of aborting the script; every module still gets scanned, the SARIF files still get merged, and the job still exits non-zero at the end if any module reported a finding. Verified locally against a two-module fixture (one clean module, one with a deliberate weak-crypto finding) using the exact script logic: the old script aborted after the second module and never produced a merged SARIF file at all; the new script scans both modules, merges both SARIF runs, and still exits 1. Separately, `if: always()` on each scanner step also let a scanner run (and report "no findings") even when checkout or Go setup had failed, which is a false-clean signal rather than real scan coverage. Add `id: checkout` / `id: setup_go` and require both to have succeeded before any scanner step (and its matching upload step) runs, while keeping the always()-based decoupling between scanners from the previous commit.
CodeRabbit finding on PR #1716: .github/workflows/pre-commit.yml's tool-cache priming step pinned gosec to v2.22.4 (cache key and go install), while ci.yml's Security Scanning job and the local pre-commit hook (scripts/gosec-hook.sh) both already run v2.28.0. GO_VERSION (1.26.5) already meets v2.28.0's toolchain requirement in both workflows, so nothing else needs to change to align upward. Bump the cache key and install version to v2.28.0 so the cached binary this step primes cannot be a stale, weaker gosec than what actually gates the build -- a developer whose local gate used the newer ruleset should not be able to pass CI on an older one, or vice versa.
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. Your included review limit is currently reached under our Fair Usage Limits Policy. Your recent PR review activity is in the 95th percentile or higher among CodeRabbit users, so adaptive limits apply. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 1 second. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/pre-commit.yml:
- Around line 130-137: Update the GOSEC_VERSION value consumed by
Makefile:install-dev-tools to v2.28.0 so local gosec scans match the pinned
version installed by the pre-commit workflow.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6e8f00d3-8b2a-4c6a-abce-0ff6d888b277
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
.github/workflows/ci.yml.github/workflows/pre-commit.yml
CodeRabbit finding on PR #1716: Makefile:16 still pinned GOSEC_VERSION to v2.22.4, the third location carrying this pin after ci.yml and pre-commit.yml (already fixed in a prior commit on this PR). `make install-dev-tools` (Makefile:244) hands a developer this version directly, so this was the one a human actually invokes. Bump to v2.28.0, keeping the `?=` so an override still works. Confirmed via repo-wide grep that no other file, Makefile target, or doc still cites v2.22.4 after this change. Full pin inventory, all five locations now agreeing: Makefile:16 GOSEC_VERSION?=v2.28.0 .github/workflows/pre-commit.yml v2.28.0 (cache key :130, install :137) .github/workflows/ci.yml v2.28.0 (:347) scripts/gosec-hook.sh 2.28.0 (:35) .pre-commit-config.yaml v2.28.0 (comments :125-126)
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 391-392: Update the SARIF merge command to write
gosec-results.sarif under $RUNNER_TEMP instead of the repository root, and
update the corresponding upload input to reference ${{ runner.temp
}}/gosec-results.sarif. Keep the existing merge behavior unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0bdecd56-7e58-4503-916f-5ca1c3d10c97
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
.github/workflows/ci.yml.github/workflows/pre-commit.ymlMakefile
CodeRabbit finding on PR #1716, ci.yml:392: the merged gosec SARIF was written to gosec-results.sarif in the checkout root, violating the repo's own coding guideline ("never save working files, text, Markdown files, or tests in the repository root"). A file sitting there is visible to any later step that walks the tree, which cuts against this PR's whole point of scanners behaving predictably. Grepped the rest of the job rather than fixing only the cited line: both Trivy scans had the same problem (trivy-results.sarif and trivy-config-results.sarif also landed in the checkout root). Moved all three merge/scan outputs to $RUNNER_TEMP (the run: steps) / ${{ runner.temp }} (the trivy-action `output:` inputs and the upload-sarif `sarif_file:` inputs) -- the env var and the context expression resolve to the same path, so the writer and reader always agree. Confirmed via repo-wide grep that nothing else reads any of the three filenames from their old checkout-root location. Re-ran the gosec set -e fixture (one clean module, one with a deliberate crypto/md5 finding) against the updated script: the merged SARIF is now written under $RUNNER_TEMP, both modules are still scanned and merged, the script still exits 1 on the real finding, and the checkout root has no stray .sarif file.
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✏️ Learnings added
✅ Action performedFull review finished. |
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. |
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. |
|
Requesting a full review, paced to one request per hour across this repo. The CodeRabbit quota is per-developer per-organization and adaptive, tightening at the 95th percentile of recent review volume. Every open PR here draws on one budget, so bursting requests both consumes it faster and tightens the limit. Spacing them is what gets verdicts to land. Full form rather than incremental: this head was pushed while the quota was exhausted, so its automatic review never ran and is not retried retroactively. An incremental request would skip exactly those commits and report clean on a diff it never read. @coderabbitai full review |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)
410-432: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winSecurity Misconfiguration (CWE-693)
Make both Trivy scans enforce the CI gate.
Set
exit-code: '1'for both scans. Trivy generates the SARIF report before returning the nonzero status. Keep both SARIF upload steps underif: always().limit-severities-for-sarif: 'true'is optional and only limits report contents toCRITICAL,HIGH.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 410 - 432, Update both Trivy scan steps, including the filesystem scan identified by trivy_fs and the IaC scan, to set exit-code to '1' so findings fail the CI job after generating SARIF reports. Preserve both SARIF upload steps with if: always(), and optionally enable limit-severities-for-sarif only to restrict report contents to CRITICAL and HIGH findings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 410-432: Update both Trivy scan steps, including the filesystem
scan identified by trivy_fs and the IaC scan, to set exit-code to '1' so
findings fail the CI job after generating SARIF reports. Preserve both SARIF
upload steps with if: always(), and optionally enable limit-severities-for-sarif
only to restrict report contents to CRITICAL and HIGH findings.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 050c374a-f6b1-416d-b29d-bb12f23f89a6
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
.github/workflows/ci.yml.github/workflows/pre-commit.ymlMakefile
Two new high-severity npm advisories appeared since #1716 patched brace-expansion and fast-uri, reddening `npm audit --audit-level=high` on main and every open PR: - js-yaml (GHSA-5p4m-2wfm-xmqj): quadratic CPU consumption in !!omap resolution, pulled in transitively by eslint and by ts-jest's @istanbuljs/load-nyc-config. - nanoid (GHSA-2v37-7h3g-55p8): custom generators can loop indefinitely when size is zero, pulled in transitively by css-loader's postcss. Neither package is a direct dependency; both resolve within the existing semver ranges declared by their parents. `npm audit fix` bumped only the three affected lockfile entries (js-yaml 3.15.0 to 3.15.1, js-yaml 4.3.0 to 4.3.1, nanoid 3.3.16 to 3.3.18) with no change to package.json. Verified `npm audit --audit-level=high` exits 0 after the fix, `npm run build` succeeds, and the test suite is no worse: 8 failed / 2773 passed / 1 skipped, identical on this branch and on unmodified origin/main. The 8 failures are a pre-existing host-locale thousands-separator mismatch in riexchange.test.ts / approval-details.test.ts, filed as #1728. Refs #1712
…ning (#1716) * fix(frontend): patch npm audit high-severity advisories in lockfile brace-expansion (GHSA-rgw5-rvv9-x895) and fast-uri (GHSA-7p8r-x3mc-p8w7) were flagged as high severity by npm audit, failing Security Scanning on every PR regardless of frontend changes. Both are transitive dependencies; npm audit fix bumps only the lockfile entries (brace-expansion 5.0.8 -> 5.0.9, fast-uri 3.1.4 -> 3.1.5) with no change to package.json's direct dependency ranges. npm audit --audit-level=high now exits 0, and the frontend build and test suite are unaffected by the bump. * fix(ci): stop one security scanner from disabling the others The Security Scanning job ran govulncheck, npm audit, gosec, and two Trivy scans as sequential steps in one job. GitHub Actions skips every step after a failing one by default, so a single frontend npm advisory silently disabled Go SAST (gosec) and Terraform IaC scanning (Trivy config) repo-wide, while the resulting failure pointed at a missing SARIF upload rather than the actual cause. Give govulncheck, npm audit, gosec, and both Trivy scans `if: always()` so each one runs independent of whether an earlier scanner passed. The job still fails overall if any scanner step fails, since always() does not suppress a step's own failure. Also tighten the SARIF upload steps for gosec and both Trivy scans: each now checks `steps.<scanner>.outcome != 'skipped'` before uploading, so a genuinely unrun scanner (skipped upstream of the scanner steps themselves) is tolerated without a confusing "Path does not exist" failure, while a scanner that ran and left no file still fails loud. * fix(ci): preserve gosec SARIF on real findings, gate scanners on infra success Two related CodeRabbit findings on PR #1716, both in the Security Scanning job: Closes #1717. The gosec step's per-module loop ran under `set -e`, and gosec itself exits non-zero both for a real finding and for a processing error. The first module with a finding aborted the loop before the later modules were scanned and before the final SARIF merge ran, so the upload step failed on a missing gosec-results.sarif instead of surfacing the actual finding -- the same failure class this PR's first commit fixed for cross-scanner coupling, one level in. Guard the gosec invocation in `if ! ( ... )` so a non-zero exit is recorded in a `status` variable instead of aborting the script; every module still gets scanned, the SARIF files still get merged, and the job still exits non-zero at the end if any module reported a finding. Verified locally against a two-module fixture (one clean module, one with a deliberate weak-crypto finding) using the exact script logic: the old script aborted after the second module and never produced a merged SARIF file at all; the new script scans both modules, merges both SARIF runs, and still exits 1. Separately, `if: always()` on each scanner step also let a scanner run (and report "no findings") even when checkout or Go setup had failed, which is a false-clean signal rather than real scan coverage. Add `id: checkout` / `id: setup_go` and require both to have succeeded before any scanner step (and its matching upload step) runs, while keeping the always()-based decoupling between scanners from the previous commit. * fix(ci): align pre-commit.yml's cached gosec pin with the CI gate CodeRabbit finding on PR #1716: .github/workflows/pre-commit.yml's tool-cache priming step pinned gosec to v2.22.4 (cache key and go install), while ci.yml's Security Scanning job and the local pre-commit hook (scripts/gosec-hook.sh) both already run v2.28.0. GO_VERSION (1.26.5) already meets v2.28.0's toolchain requirement in both workflows, so nothing else needs to change to align upward. Bump the cache key and install version to v2.28.0 so the cached binary this step primes cannot be a stale, weaker gosec than what actually gates the build -- a developer whose local gate used the newer ruleset should not be able to pass CI on an older one, or vice versa. * fix(ci): align Makefile's GOSEC_VERSION with the CI/pre-commit pin CodeRabbit finding on PR #1716: Makefile:16 still pinned GOSEC_VERSION to v2.22.4, the third location carrying this pin after ci.yml and pre-commit.yml (already fixed in a prior commit on this PR). `make install-dev-tools` (Makefile:244) hands a developer this version directly, so this was the one a human actually invokes. Bump to v2.28.0, keeping the `?=` so an override still works. Confirmed via repo-wide grep that no other file, Makefile target, or doc still cites v2.22.4 after this change. Full pin inventory, all five locations now agreeing: Makefile:16 GOSEC_VERSION?=v2.28.0 .github/workflows/pre-commit.yml v2.28.0 (cache key :130, install :137) .github/workflows/ci.yml v2.28.0 (:347) scripts/gosec-hook.sh 2.28.0 (:35) .pre-commit-config.yaml v2.28.0 (comments :125-126) * fix(ci): write scanner SARIF output outside the checkout root CodeRabbit finding on PR #1716, ci.yml:392: the merged gosec SARIF was written to gosec-results.sarif in the checkout root, violating the repo's own coding guideline ("never save working files, text, Markdown files, or tests in the repository root"). A file sitting there is visible to any later step that walks the tree, which cuts against this PR's whole point of scanners behaving predictably. Grepped the rest of the job rather than fixing only the cited line: both Trivy scans had the same problem (trivy-results.sarif and trivy-config-results.sarif also landed in the checkout root). Moved all three merge/scan outputs to $RUNNER_TEMP (the run: steps) / ${{ runner.temp }} (the trivy-action `output:` inputs and the upload-sarif `sarif_file:` inputs) -- the env var and the context expression resolve to the same path, so the writer and reader always agree. Confirmed via repo-wide grep that nothing else reads any of the three filenames from their old checkout-root location. Re-ran the gosec set -e fixture (one clean module, one with a deliberate crypto/md5 finding) against the updated script: the merged SARIF is now written under $RUNNER_TEMP, both modules are still scanned and merged, the script still exits 1 on the real finding, and the checkout root has no stray .sarif file.
Closes #1712
Closes #1717
What
Four fixes, landed as four commits:
frontend/package-lock.jsononly).brace-expansionandfast-uriare transitive dependencies;npm audit fixbumps only their lockfile entries.package.json's direct dependency ranges are unchanged..github/workflows/ci.yml). Givegovulncheck,npm audit,gosec, and both Trivy scansif: always()so a failing scanner no longer skips the ones after it in the same job. The job still fails overall if any scanner fails (always()does not suppress a step's own failure). The gosec/Trivy SARIF upload steps now also checksteps.<scanner>.outcome != 'skipped', so a genuinely unrun scanner is tolerated without the confusing "Path does not exist" failure, while a scanner that ran and left no file still fails loud.set -eSARIF-loss bug (fix(ci): a real gosec finding aborts its own SARIF merge under set -e and surfaces as a missing-file error #1717, found while building Setup CI/CD Process #2 and confirmed live by CodeRabbit). The gosec per-module loop ran underset -e; gosec itself exits non-zero on a real finding, which aborted the loop before later modules were scanned and before the SARIF merge ran -- so a real finding surfaced as a missing-file upload failure instead of a reported finding. Guard the gosec call inif ! ( ... )so a non-zero exit is recorded instead of aborting the script; every module still gets scanned and merged, and the job still fails if anything was found. Also addid: checkout/id: setup_goand require both to have succeeded before any scanner (and its upload) runs, so a checkout/setup-go failure can no longer look like "scanner ran, found nothing".pre-commit.yml's cached gosec pin with the CI gate.pre-commit.yml's tool-cache priming step was still pinned to gosec v2.22.4 whileci.ymland the local pre-commit hook (scripts/gosec-hook.sh) both already run v2.28.0. Bumped upward to v2.28.0 (cache key + install), matchingGO_VERSION1.26.5's toolchain support for that gosec release in both workflows.Before / after: npm audit
Before (reproduced against
origin/mainat02702a108, matching the issue):After:
Lockfile diff is exactly the two transitive bumps, no direct dependency ranges touched:
npm run build(webpack production) still succeeds.npm teststill passes at the same baseline: 2773 passing, 8 pre-existing failures insrc/__tests__/{approval-details,utils,riexchange}.test.tscaused by this sandbox's non-en-USICU locale rendering$1,200as$1.200. Confirmed these 8 failures are identical (same tests, same count) on unmodifiedmainbefore the lockfile change, so they are pre-existing and environment-caused, not introduced by this PR.Decoupling evidence
I don't have permission to trigger a real GitHub Actions run from here, so I built and executed a local
act(v0.2.89, via Docker) simulation that reproduces the exactif:expressions used inci.yml, to get real executed evidence instead of just reasoning about the YAML.Scenario A - mirrors the actual bug: an
npm-audit-simstep that deliberately fails, withgosec-sim,trivy-fs-sim, andtrivy-iac-simsteps (eachid: <name>,if: always()) and matchingupload-*-simsteps (if: always() && steps.<name>.outcome != 'skipped') after it, matching the shape of the real job:Every scanner after the failing one still ran and its upload found the file. The job still reports failed overall, because
always()only unblocks later steps, it does not suppress the failing step's own outcome.Scenario B - a scanner step that is genuinely never run (
if: false, standing in for something like a checkout/setup-go failure upstream of the scanner steps themselves):No "Path does not exist" failure:
steps.gosec.outcome != 'skipped'correctly evaluates false when the scanner step itself never ran, so the upload step is skipped too instead of erroring on a missing file.#1717 fix: evidence
CodeRabbit flagged this on the lines this PR itself edits, and it's the same coupling defect one level in (a real finding surfacing as a missing-SARIF error), so it's fixed here rather than deferred, per @cristim.
I extracted the exact
run:script from the gosec step (with the module list swapped for a local two-module fixture:modAclean,modBwith a deliberatecrypto/md5weak-hash finding) and ran it with a locally installedgosec@v2.28.0(the pinned version), once against the OLD script and once against the NEW one.OLD script (as it existed on
mainbefore this PR):The script aborted right after modB's finding. No merge ever ran, no SARIF file was ever produced -- exactly the "Path does not exist" failure mode #1717 describes, reproduced on a real gosec finding rather than reasoned about.
NEW script (this PR):
Both modules scanned, both SARIF runs merged (
gosec-results.sarifhas 2 runs, one per module, each independently uploadable), and the script still exits 1 -- the finding still fails the job, but it is now actually reported instead of eaten by the missing-file error.Checkout/setup-go gating evidence
Added
id: checkout/id: setup_goand gated every scanner + upload step on both. Verified with a thirdactsimulation: acheckout-sim-failsstep (id: checkout, deliberatelyexit 1) followed by asetup-go-simstep and agosec-simstep gated the same way as the real job.gosec-simandupload-gosec-simnever appear in the log at all --if: always() && steps.checkout.outcome == 'success' && ...correctly skipped both, with no "Path does not exist" noise, while the job still failed overall because checkout itself failed.Verification checklist
npm audit --audit-level=highexits 0 infrontend/after the lockfile bumpfrontend/package.jsonunchanged (lockfile-only fix)npm run buildsucceedsnpm testpasses at baseline (2773/2782, 8 pre-existing locale failures confirmed present on unmodifiedmaintoo).github/workflows/ci.ymland.github/workflows/pre-commit.ymlare valid YAML (yaml.safe_load)actsimulationset -eSARIF-loss fix executed against a real gosec finding, old-vs-new, via a local fixture (not just reasoned about)actsimulation.pre-commit-config.yaml/scripts/gosec-hook.shalready pin v2.28.0; onlypre-commit.yml's cache-priming step was stale at v2.22.4Summary by CodeRabbit