ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset - #20
ci: vendor governance-enforce — this repo was never in the A_BLOCK ruleset#20yakimoto wants to merge 5 commits into
Conversation
…never in the ruleset
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b9b25fb3-4074-47de-a626-245148ac95f8) |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 44 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Comment |
PR Summary by QodoAdd governance-enforce CI gate for diff-scoped secrets/hardcoded-path checks
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
ApprovabilityVerdict: Approved 9bdfc9f Adds CI security scanning workflow. Author owns both changed files. The open review comments appear to misread the implementation (code actually falls back to empty-tree scanning, not HEAD~1) or raise theoretical concerns that fail safely rather than silently. CI-only changes with no production runtime impact. You can customize Macroscope's approvability policy. Learn more. |
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Code Review by Qodo
1.
|
Qodo FixerNo findings are within the configured fix scope. To change which findings are fixed, adjust the setting on your Qodo configuration page. |
…othing Five defects, none of them cosmetic. Refs wave-av/claude-workstation#1747. 1. FAIL-OPEN DIFF BASE. `BASE=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)` — on a root commit `git rev-parse HEAD~1` prints its unresolved argument to stdout AND fails, so the `||` branch appends and BASE becomes a two-line string. `git diff` then exits 128, and the pinned enforcer turned that into zero files and a green check. Now: a reachability-checked base (a force-push can leave `github.event.before` pointing at a commit this checkout does not have), and with no resolvable base at all it diffs against the EMPTY TREE so the whole repo is scanned rather than nothing. 2. THE PINNED ENFORCER ITSELF FAILED OPEN. `^0.4.4` resolved to 0.4.4, whose file lister is `catch { return []; }` — any git error became zero files and rendered as `OK[enforce]: 0 changed file(s) scanned — 0 A_BLOCK violations`. A git error and a clean diff were byte-identical in the output. The fix had sat unreleased on claude-workstation main since 2026-07-29 because no `governance-v*` tag was ever pushed. Released now as 0.4.6 and pinned exactly here. 3. TOKEN IN SCOPE FOR THE WRONG STEPS. `NODE_AUTH_TOKEN` was job-level, so it was also in the environment of the step that executes the downloaded package. Now step-scoped, and the .npmrc holding it is removed on exit. 4. INSTALL SCRIPTS RAN WITH THAT TOKEN. `npm install` runs preinstall/postinstall by default. Added `--ignore-scripts`. 5. CANCELLED PUSH RUNS WERE SCANNED BY NOBODY. `cancel-in-progress: true` applied to push runs, and each push run only diffs its own before..HEAD range — so a cancelled run's commits were never examined by anything. Now PR-only. Also: `timeout-minutes: 10` and `set -euo pipefail`. Receipt, against a scratch repo whose root commit carries a no-hardcoded-paths violation, simulating a branch-creation push (`before` = all zeros): old logic -> malformed base -> caught error -> [] -> OK, 0 files scanned, PASS new logic -> "no diff base resolved ... scanning the whole tree" -> BLOCK, exit 1 Credit where it is due: several of these were found by the review bots on the sibling vendoring PRs and are folded in here — the step-scoped token, the .npmrc cleanup, the exact pin, `--ignore-scripts`, the force-push reachability check, `timeout-minutes`, and the concurrency hole (5), which was crest-console#7's catch and which I had missed entirely.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d05dca0d-325e-40b6-aea2-b5b48baae926) |
| # A base can be PRESENT and still unusable: a force-push leaves `github.event.before` | ||
| # pointing at a commit this checkout no longer contains. | ||
| if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] \ | ||
| || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then |
There was a problem hiding this comment.
🟠 High workflows/governance-enforce.yml:79
When github.event.before points to an unreachable commit (e.g. after a force-push), the fallback resolves BASE to HEAD~1, so --changed scans only the final commit. Any secret or hardcoded path introduced in earlier commits of the force-pushed range is silently skipped while the job still reports success. Instead of narrowing an unresolved base to a single parent, this fallback should fail closed to the empty tree (the same path used when no base is resolved at all) so the entire tree is scanned.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @.github/workflows/governance-enforce.yml around line 79:
When `github.event.before` points to an unreachable commit (e.g. after a force-push), the fallback resolves `BASE` to `HEAD~1`, so `--changed` scans only the final commit. Any secret or hardcoded path introduced in earlier commits of the force-pushed range is silently skipped while the job still reports success. Instead of narrowing an unresolved base to a single parent, this fallback should fail closed to the empty tree (the same path used when no base is resolved at all) so the entire tree is scanned.
Evidence trail:
.github/workflows/governance-enforce.yml:67-93 @ 90c2969
…ble, not HEAD~1 Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
| if [ -z "$BASE" ]; then | ||
| BASE="$(git hash-object -t tree /dev/null)" | ||
| echo "::notice::no diff base resolved (root commit or unreachable before-sha) — scanning the whole tree against the empty tree" | ||
| fi |
There was a problem hiding this comment.
🔍 Empty-tree base assumes the enforcer uses a two-dot diff
The fail-closed fallback substitutes the empty tree object id as the diff base. This only produces "every tracked file reads as added" if @wave-av/governance internally runs git diff <base> HEAD (two-dot). If it runs git diff <base>...HEAD or git merge-base, the empty tree is not a commit and git errors out. Per the comment at .github/workflows/governance-enforce.yml:61-63, 0.4.6 fails closed on git errors, so the worst case is a red job rather than a false PASS — but it would be a hard, unexplained failure on force-pushes and root commits. Worth confirming the enforcer's diff invocation against 0.4.6.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Confirmed against the @wave-av/governance 0.4.6 source: enforce.mjs --changed uses the two-argument form git diff <base> HEAD (governance/lib/git-files.mjs:104), which git accepts for tree objects, and a regression test explicitly pins that the empty-tree base works ("changedArgs uses two-dot, which accepts a TREE"). The three-dot/merge-base failure mode cannot occur.
…l a false pass Correction to the previous commit on this branch. Refs wave-av/claude-workstation#1747. That commit replaced the fail-open `BASE=HEAD` with a fallback to `HEAD~1`. `HEAD~1` is also wrong: it scans exactly ONE commit, so a five-commit push whose base is indeterminate (branch creation, force-push, unreachable `github.event.before`) examines the last commit and reports a confident pass on the other four. A narrowed scan reported as a full pass is the same defect in a quieter costume. Receipt — scratch repo, five-commit push, violation planted in commit 1: HEAD~1 base -> OK[enforce]: 1 changed file(s) scanned -> PASS (never saw it) empty-tree base -> 5 changed file(s) scanned -> BLOCK[enforce]: no-hardcoded-paths, exit 1 Now: with no resolvable base of any kind, diff against git's empty-tree object so every tracked file reads as added and the whole repo is scanned. Loud, never partial, never empty. Credit: wave-av/wave-rig's copy on main already had this right, with the reasoning in a comment ("HEAD~1 would skip earlier commits in a multi-commit push and let a violation through"). The fan-out copied the broken shape from elsewhere and I did not check the one repo that had already solved it. Also from wave-rig: `merge_group` is now a declared trigger and `github.event.merge_group. base_sha` joins the base chain. None of these repos runs a merge queue today, so the trigger is inert — but a required check that never reports on an event the repo actually uses is a permanent deadlock, and this closes that in advance rather than after someone hits it.
Adds the
governance-enforceA_BLOCK gate (secrets / hardcoded-paths, diff-scoped) to this repo. Part of claude-workstation#1624 E4 T4.9a, following thewave-av/cli#20pilot.Why this repo had no secrets scan
The org ruleset
governance-a-block-enforce(17901847) requires anenforcecheck across the fleet. Its scope is an explicit include list of 112 hand-maintained repository names — and every one of them matcheswave-*.The 16 public repos absent from that list are exactly the 16 not named
wave-*:.github,adk,api-spec,cli,companion-module-wave,create-wave-app,crest-console,dispatch-edge,examples,mcp-server,obs-wave-plugin,sdk,sdk-python,sdks,vmix-wave-integration,workflow-sdk.Read the intersection: the repos that publish our npm packages are precisely the repos running with no A_BLOCK secrets scan. Nobody excluded them. A naming convention silently became a security boundary, and it drew the line in the worst possible place.
Why the workflow lands before the ruleset entry
Adding a repo to a
required_status_checksruleset before it emits that check is a permanent deadlock — a required check that never reports can never go green, and every PR on the repo becomes unmergeable. So the order is: vendor the workflow, observe it green, then extend the list. Doing it the intuitive way round would have bricked all sixteen.This PR is also its own liveness drill. The workflow triggers on
pull_request, so it runs on the PR that adds it. Ifenforcereports green here, the vendored shape works in this repo. If it does not, nothing was required and nothing is blocked — which is the point of this ordering.Proven before fan-out, not assumed
@wave-av/governanceis aninternal-visibility package owned byclaude-workstation, so whether a public repo'sGITHUB_TOKENcan read it was the one real assumption. Rather than fan out on the inference, it was piloted on a single repo first:That is the receipt this PR rides on. The shape is copied verbatim from
wave-av/wave-moq-edge(public, 12/12 green), which matters becauseauto-approve.ymlfails silently on every public repo — it calls a reusable workflow in the privatewave-foundation, and a public repo cannot do that (parse-time failure, zero jobs, no annotation). This workflow calls nothing cross-repo, so that trap does not apply.Security properties, unchanged from the source:
actions/checkout@df4cb1c,actions/setup-node@48b55a0)persist-credentials: falseon checkoutcontents: read+packages: readRUNNER_TEMP,--no-save, so nothing touches this repo's dependency tree.npmrcis written with a literal${NODE_AUTH_TOKEN}(single-quotedprintf) which npm expands at run time — no secret value is ever written to disk or a log${{ }}inputs (base.sha,event.before) are routed throughenv:and read as"$VAR", never interpolated into the script bodyDiff-scoped by design: it blocks new violations without failing on legacy debt.
Refs wave-av/claude-workstation#1624.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.Note
Low Risk
CI-only change with hardened install and fail-closed diff logic; no application runtime or merge rules until the org ruleset is updated after a green run.
Overview
Adds
governance-enforceCI so this repo gets an A_BLOCK diff scan (secrets and hardcoded paths via@wave-av/governance) before it is added to the orggovernance-a-block-enforceruleset.The workflow runs on PRs and pushes to
main/master, installs@wave-av/governance@0.4.6into an isolated temp directory (scoped registry token,--ignore-scripts, SHA-pinned actions), and runsenforce.mjs --changedagainst the PR base or push range. Concurrency cancels in-progress PR runs but not push runs, so cancelled push jobs do not leave commits unscanned. If no diff base resolves, it fails closed by diffing against the empty tree instead of reporting green with zero files scanned.CHANGELOG records the new workflow under [Unreleased].
Reviewed by Cursor Bugbot for commit 90c2969. Configure here.
Note
Add governance-enforce CI workflow to scan for secrets and hardcoded paths
@wave-av/governance@0.4.6on pull requests, merge groups, and pushes to main/master.--ignore-scriptsand a temporary.npmrcscoped to that step; the diff base is resolved from the PR/merge-group/push SHA, falling back to the empty-tree (full-repo scan) if no valid base is found.A_BLOCKruleset.Macroscope summarized 9bdfc9f.