fix(ci): grant Scorecard's called workflow contents:read (last startup_failure)#294
Merged
Conversation
…oven checkout bump Two independent CI fixes, both measured on main after #293. 1. `Scorecards supply-chain security` -- the last startup_failure Re-pinning scorecard-reusable in #293 did NOT fix it, which proves the pin was never the cause. Root cause is the caller's permission block: jobs: analysis: permissions: security-events: write id-token: write # <- contents omitted Declaring `permissions:` at job level sets every UNLISTED scope to `none`, and a called workflow can never hold more than the calling job grants it. scorecard-reusable declares top-level `permissions: contents: read` and its first step is actions/checkout, so it was being handed `contents: none` -- the call died at startup with zero jobs, which is why there is no log to read. Confirmed against the green sibling: boj-server-cartridges' scorecard.yml lists all three (`contents: read`, `security-events: write`, `id-token: write`) and its runs succeed. Adding `contents: read` here. This is root cause (A) from the estate's two startup_failure shapes -- reusable permission escalation -- not BROKEN-M1. 2. Revert pages.yml/pages-deploy.yml checkout to the v4 SHA #293 pinned these to de0fac2e (v6.0.2). Both workflows are `push: [main]`-only, so no PR could exercise them, and pages.yml is this repo's only container job (idris2-pack) and has never completed a run -- checkout v6 needs node24 injected into the container, which is precisely what could not be observed. 11d5960a is exactly what `actions/checkout@v4` resolves to today, so this is a pure pin: the linter only requires a full-length SHA, not a particular version. A considered v4 -> v6 bump belongs in a change where it can be watched. Verified: 0 of 94 `uses:` unpinned; all three files parse as YAML.
🔍 Hypatia Security ScanFindings: 215 issues detected
View findings[
{
"reason": "Issue in build.yml",
"type": "missing_timeout_minutes",
"file": "build.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in pages-deploy.yml",
"type": "missing_timeout_minutes",
"file": "pages-deploy.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in push-email-notify.yml",
"type": "missing_timeout_minutes",
"file": "push-email-notify.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in instant-sync.yml",
"type": "secret_action_without_presence_gate",
"file": "instant-sync.yml",
"action": "peter-evans/repository-dispatch",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in codeql.yml",
"type": "codeql_missing_actions_language",
"file": "codeql.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/hesiod-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
marked this pull request as ready for review
July 21, 2026 14:52
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.
Two independent fixes, both measured on
mainafter #293 landed.1 ·
Scorecards supply-chain security— the laststartup_failureRe-pinning
scorecard-reusablein #293 did not fix it. That is the useful result: it proves the stale pin was never the cause.The real cause is the caller's permission block:
Declaring
permissions:at job level sets every unlisted scope tonone, and a called workflow can never hold more than the calling job grants it.scorecard-reusabledeclares top-levelpermissions: contents: readand its first step isactions/checkout— so it was being handedcontents: none. The call died at startup with zero jobs, which is exactly why there is no log to read and why it looked inscrutable.Confirmed against the green sibling.
boj-server-cartridges'scorecard.ymllists all three —contents: read,security-events: write,id-token: write— and its runs succeed. This one omitted the first.This is root cause (A) of the estate's two
startup_failureshapes — reusable permission escalation — and is unrelated to BROKEN-M1, which was the other.2 · Revert
pages.yml/pages-deploy.ymlcheckout to the v4 SHA#293 pinned these to
de0fac2e(v6.0.2). On reflection that shipped an unvalidated version change:push: [main]-only — nopull_request— so no PR can exercise them. The bump's first execution is onmain.pages.ymlis this repo's only container job (idris2-pack) and has never completed a run. checkout v6 requires node24 injected into the container; whether that works against this image is precisely what could not be observed.11d5960ais exactly whatactions/checkout@v4resolves to today, so this is a pure pin — the linter only requires a full-length SHA, not a particular version. A considered v4 → v6 bump belongs in a change where it can be watched.Verification
uses:unpinned (scanning both the bare and- uses:forms — the latter is invisible to the standards linter)State of
mainafter #293Governanceis SUCCESS — all 9 jobs green, the two that were red are fixed. Remaining red onmain:Scorecards supply-chain securitycontents: noneto the called workflowdeploy(Cloudflare)CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_IDabsent from repo secrets — confirmed in the log: "necessary to set a CLOUDFLARE_API_TOKEN environment variable"SonarQubeSONAR_TOKENexists butHTTP 403; the SonarCloud project itself is fine (badge endpoint 200), so the token is expired/wrong-scope🤖 Generated with Claude Code