Measure agentic-actions-auditor against zizmor, add a workflow and evals - #241
Open
mkzung wants to merge 2 commits into
Open
Measure agentic-actions-auditor against zizmor, add a workflow and evals#241mkzung wants to merge 2 commits into
mkzung wants to merge 2 commits into
Conversation
The plugin documents nine vectors, but nothing said how often they occur or whether zizmor already covers them, and zizmor is not mentioned anywhere in it. Measured over 60 workflows invoking claude-code-action, run-gemini-cli, codex-action or ai-inference, each pinned by content SHA: zizmor reports unpinned-uses across 87 findings in 46 files, artipacked 49 in 43, template-injection 12 in 4, and nothing at all in 8 of the 60. Of the six statically decidable vectors, F occurs in 16 workflows, B in 2, H in 1, and the rest in none. The two sets are disjoint. Every template-injection finding points at a run: step and none at a prompt field. Neither of the two workflows where a github.event expression reaches an agent prompt drew that rule; one drew nothing at all, the other only unpinned-uses and github-app. workflows/audit-agentic.js runs discovery, then four sweeps in parallel by vector family, then the report. Three of its four exits are refusals; the one that matters is no-agent, since the skill's own When NOT to Use puts a workflow with no AI action out of scope. tests/vectors.py carries a signature per decidable vector, each with a fixture that must match and a safe twin that must not; C, E and G get none, because deciding them needs the meaning of a prompt. measure.py refuses to print counts if that self-test fails, and workflow_smoke.mjs drives the workflow with stubbed agents to check every exit is reachable and no sweep family goes missing or fails silently. corpus/ is gitignored and no repository is named. Committing a third party's exploitable workflow as a fixture would publish a list of live targets, so every fixture is written by hand. Two things a reviewer should weigh rather than take on trust. The F signature reported zero until it was keyed on claude_args --allowedTools instead of an allowed_tools: input, which claude-code-action dropped after v0.0.32 and which the vector-f reference never named. Corrected, F is the most common vector in the corpus, and the case ordering follows the corrected number. The five cases do not discriminate, and the problem is the premise. Given only the fixture, an agent with no reference material found the prompt injection in 01, traced the env-var flow in 02, and on a fixture built to separate the arms on F said unprompted that a prefix allowlist is permeable to command substitution, and on a fourth where the agent was hidden inside a composite action followed the uses: reference and found it anyway. Four fixtures, four no-plugin passes. So the vectors are not scarce knowledge for a current model, and no rewrite of the fixtures fixes that. evals/README.md sets out where uplift plausibly remains instead: recall across a directory rather than depth on one file, agents reached only through a composite action, consistent labelling, and the refusal. Worth an opinion before the cases are rebuilt; trailofbits#229 deleted a case for the same reason.
mkzung
force-pushed
the
evals/agentic-actions-auditor
branch
from
August 6, 2026 14:17
8d9a0e4 to
7395789
Compare
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.
agentic-actions-auditordocuments nine vectors, but nothing said how often they occur or whether zizmor already covers them, and zizmor is not mentioned anywhere in the plugin. This measures that first, then converts the skill to a workflow and adds an eval suite shaped by the answer.What the corpus says
60 workflows invoking
claude-code-action,run-gemini-cli,codex-actionorai-inference, each pinned by content SHA. zizmor covers the general surface heavily:unpinned-uses87 findings across 46 files,artipacked49 across 43,template-injection12 across 4, and nothing at all in 8 of the 60.Of the six statically decidable vectors,
Foccurs in 16 workflows,Bin 2,Hin 1, andA,DandIin none.The two sets are disjoint. Every
template-injectionfinding points at arun:step and none at a prompt field. Neither of the two workflows where agithub.eventexpression reaches an agent prompt drew that rule; one drew nothing at all, the other onlyunpinned-usesandgithub-app. That is the case for the plugin existing, and it is now measured rather than asserted.corpus/is gitignored and no repository is named anywhere. Committing a third party's exploitable workflow as a fixture would publish a list of live targets, so every fixture here is written by hand.What is in the PR
workflows/audit-agentic.jsruns discovery, then four sweeps in parallel by vector family, then the report. Three of its four exits are refusals. The one that matters isno-agent: the skill's own When NOT to Use puts a workflow with no AI action out of scope, so the run stops and points at general tooling rather than producing a findings report organised by agentic vectors about a workflow that has no agent.tests/vectors.pycarries a signature per decidable vector, each paired with a fixture that must match and a safe twin that must not. C, E and G get none on purpose: deciding them needs the meaning of a prompt or the behaviour of a later step.measure.pyrefuses to print counts if that self-test fails, andworkflow_smoke.mjsdrives the workflow with stubbed agents to check every exit is reachable and that no sweep family goes missing or fails silently.Two things to weigh rather than take on trust
The
Fsignature reported zero until it was keyed onclaude_args --allowedToolsinstead of anallowed_tools:input, whichclaude-code-actiondropped after v0.0.32 and which the vector-f reference never named. Corrected,Fis the most common vector in the corpus. The case ordering follows the corrected number; before the fix I had it backwards.The five cases do not discriminate, and the problem is the premise.
claude plugin evalis in early access here, so instead I ran each fixture in isolation against a fresh agent with no reference material:Fuses:reference, found the agent and the injectionFour fixtures, four no-plugin passes. So the nine vectors are not scarce knowledge for a current model, and neither rewriting the fixtures nor hiding the agent behind another file changes that.
evals/README.mdsets out what is left: recall across a directory of many workflows rather than depth on one file, which is about stamina rather than knowledge and is the one hypothesis the four failures above do not settle; consistent labelling so findings can be counted; and the refusal, which the workflow enforces structurally rather than asking the model for. I would rather have your opinion on that before rebuilding the cases, since #229 deleted a case for the same reason.One small thing the pilot turned up: the unaided arm pointed out that
secretsis unavailable inside composite actions, so ananthropic_api_key: ${{ env.SOMETHING }}there silently resolves to empty. That matches GitHub's documentation andcross-file-resolution.mddoes not mention it, though it covers resolving and input tracing. Happy to add it in a separate PR if useful.make self-test,make lintandmake validatepass.make python-testsfails the same way on a clean checkout, inconstant-time-analysis. Note thatvalidate_reference_linksskips any path withevalsin it, so validate does not actually cover that directory.Written with Claude Code, reviewed and verified by me.