Skip to content

ci(claude): clarify --allowedTools is convenience, not enforcement#473

Merged
heskew merged 1 commit intomainfrom
workflow/honest-allowlist-comments
May 5, 2026
Merged

ci(claude): clarify --allowedTools is convenience, not enforcement#473
heskew merged 1 commit intomainfrom
workflow/honest-allowlist-comments

Conversation

@heskew
Copy link
Copy Markdown
Member

@heskew heskew commented May 5, 2026

Summary

Spike (queued from PR #452 followups) found that `Bash(grep -rn …)` calls all executed in the failed run despite not matching any listed `Bash(...:*)` pattern — `permission_denials: []` for the entire session.

Per claude-code-action's own `docs/configuration.md` (v1.0.110):

The base GitHub tools are always included. Use `--allowedTools` to add additional tools, and `--disallowedTools` to prevent specific tools from being used.

So `--allowedTools` is additive, not exclusive. In CI's non-interactive mode (no `canUseTool` callback set by the action), the SDK's `default` permission mode falls through to "execute" rather than prompting. Tools not listed still execute.

What this PR fixes

The comment block in `claude-mention.yml` claimed "Tool allowlist is a security boundary — every entry is a potential RCE primitive if a prompt injection succeeds." Overstated. The allowlist doesn't gate; the actual containment is elsewhere. The "deliberately absent / tightened" entries (`Bash(npx:*)` absent, `Bash(npm install)` no-arg) are documentation of intent, not enforcement — an injected instruction running them would execute.

`claude-issue-to-pr.yml` carried a referencing comment with the same overstatement.

This PR replaces both comment blocks with an honest account of where real containment lives:

  1. Token scope (repo-scoped; no cross-repo reach)
  2. Branch protection on `main` / `release_` / `v.x`
  3. Auth gate (CODEOWNERS-driven team check) — narrow trigger surface
  4. Allowed labels list (issue-to-pr only)
  5. Runner ephemerality
  6. Prompt-injection guards in the prompt itself

The notable-absences notes are kept but reframed as prompt-level discipline rather than allowlist enforcement.

What this PR does NOT fix

`claude-review.yml` in this repo has the same misleading comment. Not touching it here because it'll be replaced by the caller pattern when `HarperFast/ai-review-prompts#8` (reusable workflow) and the harper-migration-to-caller PR land. Fixing it here is unnecessary churn.

PR #8 carries the corrected comment in the reusable `_claude-review.yml` (force-pushed earlier today).

Practical risk

Low. The agent in practice doesn't try dangerous things because the prompt discipline doesn't ask it to, and the actual containment (token scope, branch protection, runner ephemerality) is real. The misleading comments are a documentation correctness issue, not a behavioral one.

Track 2 implication

When we build our own agent on Harper Fabric, we don't have to inherit claude-code-action's permissive default. Wire deny-by-default at the orchestrator level + a `canUseTool`-equivalent in Harper code. Captured in memory for the next-week design conversations.

Test plan

  • No behavior change. Comments-only.
  • Confirm `Auth gate invariants / validate` still passes (it touches `claude-*.yml` so the validator runs; structural invariants unchanged).

🤖 Generated with Claude Code

Spike on PR #452's failed run found that `Bash(grep -rn …)` calls
all executed (`permission_denials: []`) despite not matching any
listed `Bash(...:*)` pattern. Per claude-code-action's own
`docs/configuration.md` (v1.0.110): "The base GitHub tools are
always included. Use `--allowedTools` to add additional tools,
and `--disallowedTools` to prevent specific tools from being
used." `--allowedTools` is ADDITIVE, not exclusive. In CI's
non-interactive mode (no `canUseTool` callback), the SDK's
`default` permission mode falls through to "execute" rather
than prompting.

So the comment block in claude-mention.yml claiming "Tool
allowlist is a security boundary — every entry is a potential
RCE primitive if a prompt injection succeeds" is overstated.
The allowlist doesn't gate; the actual containment is elsewhere.
The "deliberately absent / tightened" entries (`Bash(npx:*)`
absent, `Bash(npm install)` no-arg) are documentation of intent,
not enforcement — an injected instruction that ran them would
still execute.

This commit replaces those comment blocks in both
claude-mention.yml and claude-issue-to-pr.yml with honest
accounts of where containment actually lives:

  1. Token scope (repo-scoped, no cross-repo reach).
  2. Branch protection on protected refs.
  3. Auth gate (CODEOWNERS-driven team check) — narrow trigger
     surface.
  4. Allowed labels list (issue-to-pr only).
  5. Runner ephemerality.
  6. Prompt-injection guards in the prompt itself.

The notable-absences notes are kept but reframed as prompt-level
discipline rather than allowlist enforcement, with explicit
notes that real mitigation (e.g., `ignore-scripts=true` in
`.npmrc`, deferring installs to a separate CI job) is a future
concern.

Comments-only. No behavior change. Paired with
`HarperFast/ai-review-prompts#8` which fixes the same misleading
comment in the reusable `_claude-review.yml`. claude-review.yml
in this repo will be replaced by the caller pattern when #8 +
the harper migration land — fixing it here is unnecessary churn.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@heskew heskew requested review from a team as code owners May 5, 2026 20:01
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 5, 2026

Reviewed; no blockers found.

@heskew heskew merged commit 1d9f179 into main May 5, 2026
25 checks passed
@heskew heskew deleted the workflow/honest-allowlist-comments branch May 5, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants