AGENTS.md Security in Downloads: Claude Code and Codex Tested
Go deeper on this topic
SaaS integrations
Give AI the context to continue the work: source conversations, open tasks, and unresolved details, from adoption decisions to integration code.
Article 8 of 13 in this series.
Next reads
Dub conversion tracking: lead and sale APIs with Stripe customer mapping
Connect Dub clicks to signups and sales. Check Business requirements, lead and sale fields, USD/JPY amounts, invoiceId, and Stripe metadata with official sources and a credential-free JSON planner.
Dub webhook signature verification: raw bodies and replays in Node.js
Verify Dub-Signature over the received body. A Node.js/Bun Request experiment tests 36 body/signature pairs, second-read TypeErrors, and a UTF-8 BOM limit before a minimal AI handoff.
Record Slack huddles and feed AI at 22:00: six Circleback settings
A Circleback workflow for giving AI usable work context: Slack huddles, a 22:00 sync, source statements, open tasks, and adoption criteria. Separates workflow experience, documented features, and local test evidence.
Use the topic hub as a map, then continue to the next article in the series. New here? Start at the home hub →
According to the official documentation, outside a Git project Codex searches only the current directory for project instructions. Claude Code searches the launch directory and its ancestors for CLAUDE-family files and, by default, uses AGENTS.md when those are absent. A file in Downloads can therefore become instruction context. We checked the conditions in the table with harmless markers in synthetic folders.
Conclusion: inspect the launch directory and persistent configuration. Our marker test does not reproduce a compromise.
claude -pskips the workspace trust dialog, so it is unsuitable as a first inspection of an unknown folder. The read-only inventory below can help identify files to review.
Checked: 2026-10-30 (Asia/Tokyo). Tested: Claude Code 2.1.312 / codex-cli 0.163.0, macOS. The live official documentation has no fixed release version; documentation statements and local observations are distinguished below.
What was reported, and what do 90% and 50% mean?
The original Forever Security report, published October 22, 2026, describes instructions in Downloads inducing persistent configuration changes. Malware execution in more than 90% of Codex attempts and roughly 50% follow-through by Claude Code are the report’s claims. We could not find the product versions in the report; those rates cannot be transferred to our versions or treated as general success rates.
We did not test malicious code, credential transmission, or configuration modification. Our experiment measured only the appearance of harmless markers in replies.
Which instructions can load from Downloads?
The official Codex discovery rules describe searching from the project root, normally the Git root, down to cwd. Without a project root, only cwd is searched. Global instructions are considered separately; being outside Git does not remove all instructions. Custom project_root_markers settings were not tested.
The Claude Code memory documentation describes ancestor CLAUDE-family loading and default AGENTS fallback. The following are observed replies. “None” means no marker appeared, not proof that a file was never read.
| Case | Synthetic layout | Claude Code marker | Codex marker |
|---|---|---|---|
| C1 | Outside Git, cwd AGENTS.md | cwd AGENTS (twice) | cwd AGENTS |
| C2 | Outside Git, cwd CLAUDE.md | cwd CLAUDE | None |
| C3 | Outside Git, both cwd files | cwd CLAUDE | cwd AGENTS |
| C4 | Outside Git, parent CLAUDE.md only | parent CLAUDE | None |
| C5 | Parent is Git root, parent AGENTS.md only | parent AGENTS | parent AGENTS |
| C6 | C2 with -p --safe-mode |
None | Not applicable |
C5 models a dotfiles layout with the home directory as Git root; we did not run it in our real home. For nested monorepo files and imports, see the instruction discovery experiment.
Does claude -p ask before using startup configuration?
The official permissions table says print and SDK sessions never show the workspace trust dialog. Settings hooks, env and apiKeyHelper are used, and .mcp.json servers connect without asking. Disabling model tools alone does not establish a boundary around startup configuration.
Distinguish this from interactive MCP approval. See MCP configuration scopes and the codex exec input/output experiment.
Where should you look for unfamiliar changes?
- Instruction files in Downloads, Desktop and home: Origin, content and modification time; location alone is not a malware verdict
.claude/settings.json,~/.claude/settings.json: Hooks, apiKeyHelper,envkeys ending in*_BASE_URL, proxy configuration.mcp.json,~/.claude.json: MCP registrations and launch commands, including user and local scopes~/.codex/config.toml:mcp_servers, command/args andchatgpt_base_url
The Codex configuration reference distinguishes user configuration from trusted project .codex/config.toml. Project config cannot override chatgpt_base_url. We did not test writing to real configuration files.
Save and read the read-only inventory script, then run it with Node 22 or later:
node agent-launch-selfcheck.mjs --home "$HOME"It makes no network requests or file writes. It reports instruction-file sizes, timestamps and SHA-256 hashes, relevant settings key names, and simple TOML MCP names with command basenames. It omits env values, URL values and complete arguments. On macOS it also checks the quarantine attribute.
Exit 0 means no findings in its limited scope, 1 means items to review, and 2 means an input/read error. A finding is not a compromise verdict. The line-oriented TOML check does not cover quoted table names, multiline syntax or inline tables. It does not scan arbitrary working directories, settings.local.json, custom configuration homes or every ancestor. Exit 0 is not a safety guarantee. Output contains local paths; inspect it before sharing. In ~/.claude.json, it reads only top-level mcpServers, not per-project MCP settings under projects.
How should you open an unfamiliar folder?
First inspect its files and startup configuration with an ordinary editor. Claude Code 2.1.312 help describes --safe-mode as disabling customizations including CLAUDE.md, hooks and MCP. Managed policy still applies, and tool permissions remain separate; we measured only the CLAUDE.md marker in print mode. AGENTS.md and interactive safe mode remain untested; safe mode does not replace a sandbox.
claude --safe-mode
codex exec -s read-only --skip-git-repo-check "List the files; do not modify them."--skip-git-repo-check permits execution outside Git; it does not disable instructions. Keep the read-only boundary as described in the official security guidance. This command still considers the working directory’s instruction files. Do not rely on a prompt appearing in a noninteractive run; inspect MCP startup configuration first. Also distinguish written instructions from hook-based action controls.
What did this experiment establish, and what remains untested?
The canary script creates synthetic fixtures under /tmp/agent-launch-*. Agent-facing inputs are Markdown only, apart from C5 Git metadata. Trusted recording-hook settings live outside the fixtures. Execution is optional and includes CLI model calls:
node agent-launch-canary.mjs --run > observations.jsonClaude used --tools "" --strict-mcp-config --setting-sources project,local; Codex used -s read-only --ignore-user-config --ephemeral. Claude C1 ran twice; every other applicable case ran once. We saved replies, exit codes and the observation JSON. Direct AGENTS loading does not fire InstructionsLoaded according to the docs, so replies provide supporting evidence. C2, C3 and C4 also recorded CLAUDE session_start load events. Before/after SHA-256 matched for the full config.toml and settings.json files and the mcpServers portion of .claude.json. Authentication context and Codex global instructions were not fully isolated. Codex stderr contained errors indicating existing MCP connection attempts even with read-only. Connection success and the configuration source remain unverified. The public JSON redacts endpoint URLs; Codex stdout contains only the final message extracted from JSONL. Your own canary output contains local paths and diagnostics; review it before sharing. This does not test attack resistance, interactive approval, writing to real configuration, or all product versions.
Canary SHA-256:
c35120a9c12361ca626f392285829e3f2ce307a266e29b2d0a4f8f18c35f9761Selfcheck SHA-256:
67e1cefef59602d2ed9520b02223bb70f90e9b1ae3cb86c4eaff67e82bfa5f22Synthetic-home tests confirmed no writes and redacted values; tests detected variants that skip Downloads or expose env values.
FAQ
- Q. Does Codex read AGENTS.md outside Git?
- The docs say that without a project root only the current directory is searched. Our 0.163.0 non-Git fixture produced the cwd AGENTS marker. Global instructions are considered separately.
- Q. Does Claude Code read AGENTS.md in Downloads?
- By default it reads AGENTS.md when no CLAUDE-family file exists in cwd or its ancestors. Our synthetic 2.1.312 fixture produced the AGENTS marker twice. We did not run it in the real Downloads folder.
- Q. Which versions do the 90% and 50% rates describe?
- These are the Forever Security report’s claims: more than 90% for Codex and roughly 50% for Claude Code. We could not find product versions in the report. Our marker test does not remeasure those rates.
- Q. Can I check which instruction files loaded?
- Claude Code docs describe /memory and InstructionsLoaded, but direct AGENTS loading does not fire that hook. Reply markers are supporting evidence; their absence alone cannot prove a file was unread.