AP010: Risky agent extension
| Severity | high; critical for a credential sent out, medium for a piped download (low in install docs), low for a stray character or an unfinished read |
| Runs in | scan |
| One-line fix | Read the flagged line and ask the human where the extension came from. Remove hidden characters and base64-encoded commands, install tools at a pinned version instead of piping a download into a shell, and delete any instruction that sends credentials off the machine. Remove an extension nobody wrote on purpose. |
What it finds
Section titled “What it finds”Extensions are what an AI agent loads as instructions or runs as its own tools: skills, slash
commands, subagents, rules and instruction files (CLAUDE.md, AGENTS.md, GEMINI.md, Cursor
and Windsurf rules, Kiro steering, …), and installed plugins and extensions with their hooks
and scripts. Hook commands in agent settings are read too. AP010 prints four shapes:
- Hidden characters (high). Unicode tag characters, bidi overrides, bidi isolates in left-to-right text, deprecated format characters, and runs of zero-width characters or variation selectors: an AI reads them, a person reviewing the file sees nothing. A single stray zero-width character or direction mark is a low print: it hides no text.
- Credential sent out (critical). A credential’s contents go into a command that sends data
out: a key file piped into curl or nc, uploaded with curl’s
-For-Tor wget’s--post-file, copied to another host withscp, or read into a variable and sent on a later line. In instructions, a sentence that tells the agent to read a credential location (~/.ssh,~/.aws/credentials, the macOS keychain, agent history,.netrc) and to send it, with a command or in plain words (“POST it to” a web address). - Piped download (medium). A download run by a shell or interpreter that reads it, such as
the output of
curlorwgetpiped intosh, PowerShell’sirmpiped intoiex, or a file downloaded and then run with no checksum. High when the text says to keep it from the user; low in a code block under an Install, Setup or Getting started heading. - Encoded command (high). Base64 that decodes to a shell command, or a decode piped into a shell.
Each print names the file and line, and shows the command it matched with any key redacted. For hidden characters it shows only how many of each kind there are.
Why it matters
Section titled “Why it matters”An agent follows its skills and rules without asking, and plugin hooks run with your permissions. Hidden instructions, a skill that quietly uploads your SSH key, or an installer that runs whatever a server returns today all reach your machine through a file that looks harmless in a quick review.
How to fix
Section titled “How to fix”- Do not run or follow the flagged line. Ask yourself, or the person who added it, where the extension came from.
- Remove the hidden characters or the encoded command. Install tools with a package manager at a pinned version instead of piping a download into a shell. Delete any instruction that sends credentials off the machine.
- Remove an extension nobody added on purpose. If a credential may already have been sent, rotate it.
agentprint fix never edits an extension: every AP010 print is listed as yours, with the exact
file and line.
Limits
Section titled “Limits”- Each rule needs the risky shape, not a keyword. Not printed: a skill that mentions curl, a
.envfile loaded before an API call, a public key (.pub),known_hosts, sshconfigorauthorized_keys, a key named only as an option (ssh -i <key>,--kubeconfig <file>), a download piped into a script (| bash scripts/x.sh), and a sentence whose “never” or “do not” governs the command. Zero-width spaces in Thai, Lao, Khmer and Myanmar text, joiners in Hebrew, Arabic, Persian and Indic text, and bidi marks in right-to-left paragraphs are normal. - Not read:
README,CHANGELOG,LICENSEand similar files,docs/,examples/andtests/folders at an extension’s top, and dependency folders such asnode_modules. - One command line or one sentence at a time (a variable set from a credential is followed to later lines of the same script). A very long line, such as minified code, is read in overlapping parts.
- Escaped characters (a
\u200bwritten out in JSON) are not decoded. - Never a silent pass: a file over 2 MiB is read to 2 MiB, and each file has a reading budget that grows with its length (so text built to slow the scan down cannot). Whatever is left unread, the file gets a low “not fully checked” print.
- What a print shows is masked: every key, and any run of key-like characters at a line break or an edge of the snippet, since it may be a piece of a key split over two lines.
- Where each agent keeps its extensions comes from its docs; each agent module lists the paths
and the pages they come from. Not read, because the docs name no folder: Claude Desktop’s
installed desktop extensions, a global Continue rules folder, the files Kilo Code’s
instructionssetting points to, and Junie’s skills and commands.