What it detects
Audits GitHub Actions workflows that run AI agents for prompt injection and excess privilege, offline.
Agent actions (Claude Code, Codex, Gemini CLI, actions/ai-inference, home-grown claude -p / curl api.openai.com steps) are increasingly wired to issues, issue_comment and pull_request_target. That means any GitHub user can write text that lands in an LLM prompt, and that LLM often holds a write-scoped GITHUB_TOKEN, cloud secrets and a shell. This is the "prompt injection in CI" class (sometimes called *PromptPwnd*). agentci finds the AI steps in your workflows and reports how untrusted text reaches them, who can trigger them, and what they can do once hijacked.
Standard library only (it has its own small YAML-subset parser). No network.
Rules
| Rule | Default severity | Finding |
|---|---|---|
| AC01 | critical (high outside the agent step) | Untrusted event text (issue.title/body, comment.body, pull_request.title/body/head.ref, github.head_ref, commit messages, toJSON(github.event), ...) interpolated with ${{ }} into the agent step, or into a run/env/prompt-like input of another step in the AI job |
| AC02 | medium (high for *) | Outsider trigger (issues, issue_comment, pull_request_target, discussion*, workflow_run, review events) with no author_association / actor / label gate in the job or step if:. anthropics/claude-code-action checks write access itself, so it is only flagged when allowed_non_write_users / allowed_bots is set |
| AC03 | critical (high for gh pr checkout) | pull_request_target / workflow_run job that checks out the PR head (or fetches pull/N), so the agent reads attacker-controlled CLAUDE.md, AGENTS.md, scripts and configs with secrets in scope |
| AC04 | low / medium / high | AI job token scope: no permissions: block (low), write-all (high), write to contents/actions/packages/pages/... (high with outsider triggers), id-token: write (high) |
| AC05 | critical with outsider triggers, else high | Agent shell/sandbox unlocked: Bash(*) or bare Bash in allowed tools, --dangerously-skip-permissions, --dangerously-bypass-approvals-and-sandbox, danger-full-access, Codex safety-strategy: unsafe, Gemini --yolo, bypassPermissions, approvals never |
| AC06 | medium / high / critical | Secrets reachable by the agent that are not model API keys (workflow, job or step env/with), and toJSON(secrets) (critical) |
| AC07 | low (medium for @main/no ref) | AI action not pinned to a full 40-character commit SHA |
| AC08 | low | actions/checkout in the AI job without persist-credentials: false (token left in .git/config) |
| AC09 | info | Inventory: which step is an AI agent and how it was recognised |
Usage
cd tools/agentci
python3 agentci.py examples/risky # 3 workflows, 26 findings, exit 1
python3 agentci.py examples/clean --fail-on any # 2 AI workflows, 0 findings, exit 0
python3 agentci.py # defaults to ./.github/workflows
python3 agentci.py .github/workflows --sarif > agentci.sarif
python3 agentci.py . --ai-action 'acme/review-bot*' --json --min-severity mediumAfter pip install of the kit, the same options work as agentsec agentci …. Every tool also accepts --help and almost every tool accepts --json.
Example output
See the examples under tools/agentci/examples/ in the repository.
SARIF and CI
agentsec agentci path/to/input --sarif > agentci.sarif
# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
with:
tools: agentci
paths: path/to/input
fail-on: high
SARIF 2.1.0 can be uploaded to GitHub code scanning. The composite action writes per-tool reports under agentsec-sarif/ by default.
OWASP mapping
Heuristic mapping of what this tool helps a reviewer look for. Not an official OWASP endorsement, and not complete coverage of any item. Sources: OWASP Top 10 for LLM Applications 2025 and OWASP Top 10 for Agentic Applications 2026.
- LLM01 Prompt Injection — OWASP LLM Top 10 (2025)
- ASI01 Agent Goal Hijack — OWASP Agentic Top 10 (2026)
- ASI05 Unexpected Code Execution (RCE) — OWASP Agentic Top 10 (2026)
Known limits
- Heuristic and static. It reads YAML, not runtime behaviour. It does not know what an agent will do
with the text it is given, and a clean report does not mean the workflow is safe.
- AI step detection uses a list of known action names and globs plus a regex for common CLIs and API
hosts. Unusual wrappers, composite/reusable workflows (uses: ./... or workflow_call) and Docker actions are not followed; add them with --ai-action.
- Indirect injection is not traced. An agent that fetches an issue or PR body itself is still exposed
to its content; agentci only flags the trigger/gate/permission side of that (AC02, AC04, AC05). Data flowing through step outputs (steps.x.outputs.y) into prompts is not followed.
- Gate detection is a regex over
if:(author_association, actor, sender, label, ...). It cannot tell
whether a condition is actually correct.
- YAML subset. Anchors/aliases, tags and complex keys are not supported; unusual files fail with a parse
error (exit 2) instead of being guessed at. Action input names change between versions; checks use the names documented as of 2026.
claude-code-action's built-in write-access check is assumed to be on; other actions are assumed to have none.
Full source, tests, and fixtures: tools/agentci/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.