ci(security): recover Token-Permissions 0 -> 10 — measured, not inferred (#127) - #128
Merged
Merged
Conversation
) Scorecard's Token-Permissions check reads the workflow-level `permissions:` block and caps the check at 0 for the ENTIRE repository on any top-level write scope. PR #64 (2026-07-10) introduced `contents: write` + `pull-requests: write` at the top level — four days after the last run that measured this check at 10. That single line is the whole regression. The two scopes `gh pr merge --auto` actually needs now live on the one job that needs them; the workflow default is `contents: read`. Effective permissions for the job are unchanged, so auto-merge behaviour is identical. MEASURED, not inferred — OpenSSF Scorecard pinned to the same build every other estate number was produced at, v5.1.1-45-g40bbc9c9 (gcr.io/openssf/scorecard@sha256:54c7ea4ddec6e3941887cb7933898c352f59e7f59e17a7a730f97ed348a8dfce), run `--local` against a `git archive` of origin/main before and after: Token-Permissions 0 -> 10 aggregate 5.9 -> 6.7 (weights validated by reproducing the measured 5.9 from the per-check scores) The open question this measurement settled: `e2e.yml:212` carries a job-level `contents: write`, so it was genuinely unclear whether removing the top-level write would reach 10 or merely leave 0. Job-level writes are a Warn, not a cap — both remaining warns are job-level and the check still scores 10. Refs: #127 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KU225SLKQ6xHT9qMMJvZYm
`errSpy.mockRestore()` sat on the last line of the test, outside any try/finally. If an assertion above it threw, console.error stayed mocked for the remainder of that Vitest worker and silently swallowed output in every later test sharing it — a failing test poisoning its neighbours. Verified against the ground rather than assumed: vitest.config.ts sets neither `restoreMocks` nor `clearMocks`, so nothing else restores it. Provenance: a CodeRabbit finding on PR #122 that was resolved-to-unblock a live security fix rather than fixed. Resolving a thread is only honest if the item survives the click; this is that survival. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KU225SLKQ6xHT9qMMJvZYm
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe Dependabot auto-merge workflow now limits write permissions to its job, and the report-generation error test guarantees restoration of its ChangesWorkflow permission scoping
Test mock cleanup
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the first of the four categories in issue #127, per the work order
neckarshore-planning/docs/plans/2026-07-27-trustscope-127-linus-workorder.md.The change
One line of substance.
.github/workflows/dependabot-auto-merge.ymlcarriedcontents: write+pull-requests: writeat workflow level; both now sit on thedependabot-auto-mergejob, and the workflow default iscontents: read.Effective permissions for the job are unchanged — auto-merge behaves identically. Scorecard reads the top-level block and caps Token-Permissions at 0 for the whole repository on any top-level write, which is why this one line cost 7,5 weighted points.
Introduced by PR #64 on 2026-07-10, four days after the last run that measured this check at 10.
Measured, not inferred
The work order's DoD is explicit that "the YAML looks right" does not count. Method:
git archiveorigin/main into two throwaway trees, apply the candidate change to one, run Scorecard--localagainst both. The product repo was never written to during measurement.Scorecard pinned by digest to the same build every other estate number was produced at:
The aggregate figure is arithmetic, not a second live run — but the weights were validated first by reproducing the measured 5,9 exactly from the 16 applicable per-check scores, so it is arithmetic on a checked ruler.
The open question this settled:
e2e.yml:212carries a job-levelcontents: write, so it was genuinely unclear whether removing the top-level write would reach 10 or merely leave 0. It reaches 10 — job-level writes are a Warn, not a cap. Both remaining warns are job-level.Second commit: an unrelated test-hygiene rider
lib/resolve-report.test.tsrestored itsconsole.errorspy on the test's last line, outside anytry/finally. Any assertion throwing above it leftconsole.errormocked for the rest of that Vitest worker — a failing test silencing its neighbours.vitest.config.tssets neitherrestoreMocksnorclearMocks, so nothing else restored it (checked, not assumed).Provenance: a CodeRabbit finding on PR #122 that was resolved-to-unblock a live security fix rather than fixed. Carried on a board item since, with the instruction to ride the next PR touching this repo rather than spend a PR of its own. It is a separate commit so the security diff stays readable on its own.
Baseline moved — worth knowing before anyone quotes a number
Re-measured today before touching anything, as the work order's §1c required. The live aggregate is 5,9, not the 5,5 measured on 07-26: overnight Dependabot merges took Vulnerabilities from 0 to 6 (4 open advisories, down from 17).
Two further findings from that run, neither of them mine to fix here:
Maintainedscores 0 for the reason "project was created in last 90 days". That is repo age, not a defect, and it self-heals. It also carries 7,5 weight — so part of the gap to any target number is simply not buyable.Code-Reviewscores 0 — "Found 0/24 approved changesets". That is the known estate ceiling and it is category 3 of the work order, still open.Recomputed arc at today's numbers: Token-Permissions alone 6,7 · plus Vulnerabilities fully cleared 7,1 · plus Maintained ageing in 7,9.
Verification
npm run typecheck— cleannpm run lint— cleannpm test— 274 passed, 6 skipped, 33 files🤖 Generated with Claude Code
https://claude.ai/code/session_01KU225SLKQ6xHT9qMMJvZYm
Summary by CodeRabbit
Chores
Tests