anthropics / anthropics/claude-code-action

AGENTS.md is not in SENSITIVE_PATHS — the documented @AGENTS.md import loads the PR head's instructions despite the CLAUDE.md restore

Open
#1,696 1 comment 0 reactions 0 assignees View on GitHub
area:permissions bug p2
Dominant language
TypeScript
Stars
8.9k
Forks
2.1k
PR merge metrics
PR metrics pending

Description

## Summary

`SENSITIVE_PATHS` (src/github/operations/restore-config.ts, ~line 26 at v1.0.195 / `d40ddef`) restores `CLAUDE.md`, `CLAUDE.local.md`, `.claude`, `.mcp.json`, `.claude.json`, `.gitmodules`, `.ripgreprc`, and `.husky` from the base branch on PR contexts, described in-file as "Paths that are both PR-controllable and read from cwd at CLI startup" — so a PR cannot inject instructions into the run.

`AGENTS.md` is not on that list. But the Claude Code memory docs explicitly recommend keeping shared instructions in `AGENTS.md` and importing them from `CLAUDE.md` via `@AGENTS.md` (https://code.claude.com/docs/en/memory, "AGENTS.md" section). In a repo using that documented pattern, the restored base-branch `CLAUDE.md` still contains the `@AGENTS.md` import — and the import resolves against the working tree, which is the **PR head** checkout (src/github/operations/branch.ts checks out `pull/N/head`, including fork PRs). The import therefore loads the PR author's `AGENTS.md` into the run's instructions, reopening the injection path the CLAUDE.md restore was built to close.

## Impact

Bounded by the trigger gate (write access required by default, `allowed_non_write_users` empty), so exploitation requires a maintainer commenting `@claude` on a PR whose head modifies or adds `AGENTS.md`. That is precisely the scenario `SENSITIVE_PATHS` exists to defend — a maintainer invoking the action on an untrusted contribution — and the file it misses is the one the product's own docs tell users to put their instructions in. Nested `AGENTS.md` files in subdirectories (also part of the agents.md convention) have the same property when imported.

## Reproduction sketch

1. Repo with `CLAUDE.md` containing `@AGENTS.md` (the documented pattern) and both files on the default branch.
2. External PR modifies `AGENTS.md` to include adversarial instructions.
3. Maintainer comments `@claude` on the PR.
4. The action restores `CLAUDE.md` from base (correct), but the `@AGENTS.md` import inside it resolves to the PR head's file — the modified instructions load.

## Suggested fix

Add `AGENTS.md` (and arguably `AGENTS.local.md` / nested `**/AGENTS.md`, matching whatever the import + native-discovery surface grows to) to `SENSITIVE_PATHS`. More generally: any file reachable from a restored `CLAUDE.md` via `@` imports inherits CLAUDE.md's trust requirements — a recursive restore of the import closure would close the class rather than the instance, but the single filename covers the documented pattern today.

Found while auditing our own repo's adoption of the `@AGENTS.md` import pattern (action pinned at v1 → v1.0.195, installing Claude Code 2.1.235; confirmed 2.1.235 does not natively discover AGENTS.md, so the import is the sole loader — which is exactly why it escapes the restore).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.