Skip to content

fix(ci): npm audit advisories and scanner decoupling in Security Scanning - #1716

Merged
cristim merged 6 commits into
mainfrom
fix/1712-npm-audit-scanner-decoupling
Aug 5, 2026
Merged

cristim merged 6 commits into
mainfrom
fix/1712-npm-audit-scanner-decoupling

Conversation

@cristim

@cristim cristim commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

Closes #1712
Closes #1717

What

Four fixes, landed as four commits:

  1. Patch the npm audit advisories (frontend/package-lock.json only). brace-expansion and fast-uri are transitive dependencies; npm audit fix bumps only their lockfile entries. package.json's direct dependency ranges are unchanged.
  2. Decouple the Security Scanning job's scanners (.github/workflows/ci.yml). Give govulncheck, npm audit, gosec, and both Trivy scans if: 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 check steps.<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.
  3. Fix the gosec set -e SARIF-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 under set -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 in if ! ( ... ) 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 add id: checkout / id: setup_go and 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".
  4. Align 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 while ci.yml and the local pre-commit hook (scripts/gosec-hook.sh) both already run v2.28.0. Bumped upward to v2.28.0 (cache key + install), matching GO_VERSION 1.26.5's toolchain support for that gosec release in both workflows.

Before / after: npm audit

Before (reproduced against origin/main at 02702a108, matching the issue):

# npm audit report

brace-expansion  4.0.0 - 5.0.8
Severity: high
brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation - https://github.com/advisories/GHSA-rgw5-rvv9-x895
fix available via `npm audit fix`
node_modules/brace-expansion

fast-uri  3.0.0 - 3.1.4
Severity: high
fast-uri vulnerable to host confusion via backslash authority introducer - https://github.com/advisories/GHSA-7p8r-x3mc-p8w7
fix available via `npm audit fix`
node_modules/fast-uri

2 high severity vulnerabilities

To address all issues, run:
  npm audit fix
EXIT=1

After:

found 0 vulnerabilities
EXIT=0

Lockfile diff is exactly the two transitive bumps, no direct dependency ranges touched:

-      "version": "5.0.8",   +      "version": "5.0.9",    (brace-expansion)
-      "version": "3.1.4",   +      "version": "3.1.5",    (fast-uri)

npm run build (webpack production) still succeeds. npm test still passes at the same baseline: 2773 passing, 8 pre-existing failures in src/__tests__/{approval-details,utils,riexchange}.test.ts caused by this sandbox's non-en-US ICU locale rendering $1,200 as $1.200. Confirmed these 8 failures are identical (same tests, same count) on unmodified main before 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 exact if: expressions used in ci.yml, to get real executed evidence instead of just reasoning about the YAML.

Scenario A - mirrors the actual bug: an npm-audit-sim step that deliberately fails, with gosec-sim, trivy-fs-sim, and trivy-iac-sim steps (each id: <name>, if: always()) and matching upload-*-sim steps (if: always() && steps.<name>.outcome != 'skipped') after it, matching the shape of the real job:

⭐ Run Main npm-audit-sim (deliberately fails, mirrors a live advisory)
❌  Failure - Main npm-audit-sim (deliberately fails, mirrors a live advisory)

⭐ Run Main gosec-sim
| gosec ran despite npm-audit-sim failure
✅  Success - Main gosec-sim
⭐ Run Main upload-gosec-sim
| upload-gosec-sim ran, outcome=success, file exists=yes
✅  Success - Main upload-gosec-sim

⭐ Run Main trivy-fs-sim
| trivy fs ran despite npm-audit-sim failure
✅  Success - Main trivy-fs-sim
⭐ Run Main upload-trivy-fs-sim
| upload-trivy-fs-sim ran, outcome=success, file exists=yes
✅  Success - Main upload-trivy-fs-sim

⭐ Run Main trivy-iac-sim
| trivy iac ran despite npm-audit-sim failure
✅  Success - Main trivy-iac-sim
⭐ Run Main upload-trivy-iac-sim
| upload-trivy-iac-sim ran, outcome=success, file exists=yes
✅  Success - Main upload-trivy-iac-sim

🏁  Job failed

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):

[only "Set up job" and "Complete job" ran; the gosec-sim step and its
upload-gosec-sim step were both skipped by act's own if: evaluation]
🏁  Job succeeded

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: modA clean, modB with a deliberate crypto/md5 weak-hash finding) and ran it with a locally installed gosec@v2.28.0 (the pinned version), once against the OLD script and once against the NEW one.

OLD script (as it existed on main before this PR):

==> gosec in modA
==> gosec in modB
OLD_EXIT_STATUS=1
---
gosec-results-OLD.sarif does NOT exist

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):

==> gosec in modA
==> gosec in modB
::warning::gosec exited non-zero in modB (findings or a scan error)
Merged 2 SARIF runs
EXIT_STATUS=1

Both modules scanned, both SARIF runs merged (gosec-results.sarif has 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_go and gated every scanner + upload step on both. Verified with a third act simulation: a checkout-sim-fails step (id: checkout, deliberately exit 1) followed by a setup-go-sim step and a gosec-sim step gated the same way as the real job.

⭐ Run Main checkout-sim-fails
❌  Failure - Main checkout-sim-fails
⭐ Run Main setup-go-sim
✅  Success - Main setup-go-sim
✅  Success - Complete job
🏁  Job failed

gosec-sim and upload-gosec-sim never 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=high exits 0 in frontend/ after the lockfile bump
  • frontend/package.json unchanged (lockfile-only fix)
  • npm run build succeeds
  • npm test passes at baseline (2773/2782, 8 pre-existing locale failures confirmed present on unmodified main too)
  • .github/workflows/ci.yml and .github/workflows/pre-commit.yml are valid YAML (yaml.safe_load)
  • Cross-scanner decoupling executed and observed via local act simulation
  • gosec set -e SARIF-loss fix executed against a real gosec finding, old-vs-new, via a local fixture (not just reasoned about)
  • checkout/setup-go gating executed and observed via local act simulation
  • Confirmed .pre-commit-config.yaml / scripts/gosec-hook.sh already pin v2.28.0; only pre-commit.yml's cache-priming step was stale at v2.22.4
  • npm finding and gosec findings still fail the job; only the coupling between scanners (and the SARIF-loss-on-failure bug) is removed

Summary by CodeRabbit

  • Chores
    • Improved the reliability and consistency of automated security checks.
    • Security scans now run independently so available results can still be collected when another check encounters issues.
    • Enhanced validation and reporting for security scan results, including clearer handling of missing or incomplete reports.
    • Updated security scanning tools to improve vulnerability coverage and reporting quality.
    • Improved feedback when security checks are skipped, fail, or produce no results.

cristim added 2 commits August 3, 2026 23:33
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.
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint severity/high Significant harm urgency/now Drop other things impact/internal Team-internal only effort/s Hours type/bug Defect labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Security scanner workflow

Layer / File(s) Summary
Independent scanner execution
.github/workflows/ci.yml
Scanner steps continue after earlier scanner failures when checkout and Go setup succeed. Scanner IDs expose execution outcomes.
Gosec result handling
.github/workflows/ci.yml
Gosec scans all modules, records failures, validates per-module SARIF files, merges available output, and fails when no SARIF exists.
Conditional SARIF uploads
.github/workflows/ci.yml
Gosec and Trivy uploads run only when prerequisites succeed and the related scanner was not skipped.
Gosec tool version update
.github/workflows/pre-commit.yml, Makefile
The cached, installed, and default gosec version changes from v2.22.4 to v2.28.0.

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
Loading

Possibly related PRs

  • LeanerCloud/CUDly#1376: Both changes update gosec to v2.28.0; this PR updates CI and pre-commit versioning, while that PR adds a staged-file GoSec hook.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The CI decoupling and gosec SARIF requirements are met, but advisory patching cannot be verified because frontend/package-lock.json was excluded by !**/package-lock.json. Review frontend/package-lock.json to confirm brace-expansion and fast-uri are patched without changing direct dependency ranges.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the CI scanner decoupling and npm audit advisory changes in the pull request.
Out of Scope Changes check ✅ Passed All summarized changes support the linked issues, including scanner handling and the gosec v2.28.0 version alignment.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1712-npm-audit-scanner-decoupling

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for PR #1716. The review will include the current head commits.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

302-307: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Stop 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 recommends if: ${{ !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

📥 Commits

Reviewing files that changed from the base of the PR and between 02702a1 and 3e8d478.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • .github/workflows/ci.yml

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
cristim added 2 commits August 4, 2026 18:40
…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.
@cristim

cristim commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for PR #1716. The review will include the current head commits.

✅ Action performed

Full 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 02702a1 and f5468b4.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • .github/workflows/pre-commit.yml

Comment thread .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)
@cristim

cristim commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for the current head of #1716.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 02702a1 and b046a6f.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • .github/workflows/pre-commit.yml
  • Makefile

Comment thread .github/workflows/ci.yml Outdated
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.
@cristim

cristim commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for the current head of #1716. The review will include commits that incremental review could omit.


✏️ Learnings added
Learnt from: cristim
URL: https://github.com/LeanerCloud/CUDly/pull/1716

Timestamp: 2026-08-04T22:26:24.752Z
Learning: For pull request `#1716`, when review quota exhaustion prevents automatic review of pushed commits, request a full review instead of an incremental review. An incremental review can omit those unreviewed commits.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Full review finished.

@cristim

cristim commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for the current head of #1716. It will include commits that an incremental review could omit.

✅ Action performed

Full review finished.

@cristim

cristim commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for the current head of #1716. It will include commits that an incremental review can omit.

✅ Action performed

Full review finished.

@cristim

cristim commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

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

@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

@cristim Full review requested for the current head of #1716. It will include commits that incremental review can omit.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Security 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 under if: always(). limit-severities-for-sarif: 'true' is optional and only limits report contents to CRITICAL,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

📥 Commits

Reviewing files that changed from the base of the PR and between 02702a1 and 21473bf.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • .github/workflows/pre-commit.yml
  • Makefile

@cristim
cristim merged commit 0c92b77 into main Aug 5, 2026
22 checks passed
cristim added a commit that referenced this pull request Aug 7, 2026
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
cristim added a commit that referenced this pull request Sep 27, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/internal Team-internal only priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/now Drop other things

Projects

None yet

1 participant