Summary
After #276 landed, OSPS-AC-04.02 (ScopedPermissions) gets accurate detection of over-broad workflow permissions via zizmor's excessive-permissions audit. But there's no remediation path in the same control — when the audit flags a finding, darnit can only tell the user "manually narrow your permissions."
What I confirmed about zizmor --fix
Tested zizmor 1.25.2 against mlieberman85/darnit-gittuf-demo (the feature 014 demo repo):
--fix mode |
Patched |
What got fixed |
safe |
1 file |
obfuscation (unwrapped ${{ 'ubuntu-latest' }}) |
all |
4 files |
+ artipacked (added persist-credentials: false to checkout invocations) |
In both modes, excessive-permissions findings survived. Looking at zizmor's verbose output, only findings tagged note: this finding has an auto-fix get auto-fixed; excessive-permissions doesn't carry that tag. zizmor knows how to detect over-broad permissions but doesn't yet know how to programmatically narrow them — that requires authoritative "action X needs permission Y" tables that zizmor doesn't yet maintain.
So we can't just delegate to zizmor --fix.
Why this matters
enable_branch_protection is darnit's flagship example of "audit catches → tool fixes." OSPS-AC-04.02 is currently audit-only:
For the demo, a Claude Code session ended up reading the workflow source, scoping the actions' real needs, and applying the narrowing by hand — see mlieberman85/darnit-gittuf-demo#2. That worked but is exactly the kind of step darnit could automate.
Proposed approaches (pick one or sequence them)
A. LLM-led remediation handler (recommended near-term)
Add a remediation handler that builds an LLM consultation payload like darnit already does for the threat-model handler:
# Pseudo-shape
{
"action": "narrow_workflow_permissions",
"workflow_path": ".github/workflows/ci.yml",
"current_permissions": "read-all",
"zizmor_finding": { …},
"actions_used": [
{"name": "actions/checkout@…", "step_idx": 0, "with": {…}},
{"name": "actions/setup-go@…", "step_idx": 1, "with": {…}},
…
],
"instructions": "For each action, identify its minimum required GitHub
permissions. Union the required perms. Replace the
workflow- or job-level permissions block with a
minimum-needed YAML block. Verify with `zizmor` that
the finding closes."
}
The calling agent (Claude Code via MCP) does the reasoning + the file edit. The remediation handler returns a structured prompt; the agent commits + opens a PR.
Pros: cheap to add, matches darnit's existing pattern for SA-01.01 / SA-03.02 / governance docs. Cons: leans on the agent to be correct.
B. Static action → permissions map
Maintain a curated dict of {"action-name@ref": ["contents:read", …]} for common actions. Compute the union per workflow + write the narrowed block deterministically.
Pros: predictable, no LLM dependency. Cons: large curation surface, will rot as actions evolve, doesn't handle custom or proprietary actions.
C. Hybrid
Static map for common actions (covers ~80% of workflows trivially); LLM consultation path for the long tail. Best of both, more code.
D. Track upstream zizmor
Watch for excessive-permissions to graduate into --fix-eligible. If/when it does, AC-04.02 can simply pipeline zizmor --fix=safe as a deterministic remediation step. No bet on timeline.
Suggested acceptance for option A (smallest workable PR)
- New entry in AC-04.02's
[remediation] block with a handler = "llm_remediation" (or similar; same plumbing as threat-model verification prompts).
- Handler emits the payload above.
- Darnit's
darnit-remediate skill knows how to consume it: read the workflow, propose a minimum-permissions YAML diff, apply on a branch, run zizmor, open PR.
Related
Out of scope (for this issue)
- Doing the same for other zizmor audits (impostor-commit, unpinned-uses, dangerous-triggers) — those map to other OSPS controls and warrant their own remediation tracks.
- Building a generalised "any zizmor finding → LLM remediation" pipeline. The current issue is specifically the AC-04.02 gap; broader integration can be considered later.
Summary
After #276 landed,
OSPS-AC-04.02(ScopedPermissions) gets accurate detection of over-broad workflow permissions via zizmor'sexcessive-permissionsaudit. But there's no remediation path in the same control — when the audit flags a finding, darnit can only tell the user "manually narrow your permissions."What I confirmed about zizmor --fix
Tested zizmor 1.25.2 against
mlieberman85/darnit-gittuf-demo(the feature 014 demo repo):--fixmodesafeobfuscation(unwrapped${{ 'ubuntu-latest' }})allartipacked(addedpersist-credentials: falseto checkout invocations)In both modes,
excessive-permissionsfindings survived. Looking at zizmor's verbose output, only findings taggednote: this finding has an auto-fixget auto-fixed;excessive-permissionsdoesn't carry that tag. zizmor knows how to detect over-broad permissions but doesn't yet know how to programmatically narrow them — that requires authoritative "action X needs permission Y" tables that zizmor doesn't yet maintain.So we can't just delegate to
zizmor --fix.Why this matters
enable_branch_protectionis darnit's flagship example of "audit catches → tool fixes."OSPS-AC-04.02is currently audit-only:excessive-permissionsdeep audit (primary) #276)enable_*MCP tool, noapi_call/file_createhandler in TOMLFor the demo, a Claude Code session ended up reading the workflow source, scoping the actions' real needs, and applying the narrowing by hand — see mlieberman85/darnit-gittuf-demo#2. That worked but is exactly the kind of step darnit could automate.
Proposed approaches (pick one or sequence them)
A. LLM-led remediation handler (recommended near-term)
Add a remediation handler that builds an LLM consultation payload like darnit already does for the threat-model handler:
The calling agent (Claude Code via MCP) does the reasoning + the file edit. The remediation handler returns a structured prompt; the agent commits + opens a PR.
Pros: cheap to add, matches darnit's existing pattern for SA-01.01 / SA-03.02 / governance docs. Cons: leans on the agent to be correct.
B. Static action → permissions map
Maintain a curated dict of
{"action-name@ref": ["contents:read", …]}for common actions. Compute the union per workflow + write the narrowed block deterministically.Pros: predictable, no LLM dependency. Cons: large curation surface, will rot as actions evolve, doesn't handle custom or proprietary actions.
C. Hybrid
Static map for common actions (covers ~80% of workflows trivially); LLM consultation path for the long tail. Best of both, more code.
D. Track upstream zizmor
Watch for
excessive-permissionsto graduate into--fix-eligible. If/when it does, AC-04.02 can simply pipelinezizmor --fix=safeas a deterministic remediation step. No bet on timeline.Suggested acceptance for option A (smallest workable PR)
[remediation]block with ahandler = "llm_remediation"(or similar; same plumbing as threat-model verification prompts).darnit-remediateskill knows how to consume it: read the workflow, propose a minimum-permissions YAML diff, apply on a branch, run zizmor, open PR.Related
excessive-permissionsdeep audit (primary) #276 (AC-04.02 zizmor primary).Out of scope (for this issue)