Report privately through GitHub, on the Security tab of this repository. That opens a draft advisory only the maintainers can see. Please do not open a public issue for a vulnerability.
Say what you can reach and how, and include the config or command that reproduces it. A first reply should take a few days.
Only the latest published version is supported. There are no maintained release branches, so a fix ships as a new release rather than as a backport.
agent-reference turns names into readable source on disk. It runs git and reads the npm registry, and it does so with values it did not write: a repository URL out of registry metadata, a directory out of a package's manifest, a ref out of a config file someone else committed. Those are the interesting inputs, and the ones a report is most likely to be about.
The boundaries it holds, each enforced in src/git.ts:
- Every git invocation is built from one argv.
gitArgvapplies the transport policy, so no caller can spawngitaround it. ext::transports are refused outright, andfiletransports stay at git'suserdefault.ext::runs an arbitrary command as a transport, and CI images do relax git's defaults, so this is stated rather than inherited.- No value reaching argv may begin with
-. git reads such an argument as an option wherever it sits, and--upload-pack=<cmd>turns a fetch into code execution that no protocol policy stops. - A repository URL must use https, http, ssh, git, or a local path. Anything else is refused before a store path is derived from it.
- git never waits for a human.
GIT_TERMINAL_PROMPT=0, so a private or missing repository fails rather than sitting in a credential prompt. - A
directorycannot climb out of its checkout. Both the configured subtree and the one a package's own manifest declares are normalized and then checked for containment against the resolved path. - Relayed text is stripped of control characters. Registry errors, git's stderr, and config descriptions all reach a terminal and an agent's context, where control bytes reposition a cursor and can shape text like instructions.
initinterpolates nothing it read. The brief it prints is a prompt handed to an agent, so only valuesinitcomputed itself go into it; a checked-in file cannot write instructions to a future agent in this tool's voice.
Two commands read history rather than source. audit opens the session transcripts your harnesses already wrote, under ~/.claude/projects, ~/.codex/sessions, and opencode's storage directory, counts four patterns in each, and drops them: nothing is written, nothing is cached, and what survives the walk is four numbers per store and one quoted line under each count. Those quotes are somebody else's text on its way to a terminal and an agent's context, so they are stripped of control characters like every other relayed string here. activity reads only the log this tool wrote about its own runs, at <store>/log/usage.jsonl. Neither one sends anything anywhere, and AGENT_REFERENCE_NO_LOG=1 stops the recording that activity reads.
Materialized source is not sandboxed. A checkout is upstream's files on your disk, and reading them is the point; nothing here executes them, and neither should anything downstream without deciding to.
Credentials are the ambient ones. agent-reference clones with your own git configuration and never stores, prompts for, or transmits a credential.
- A reference resolving to source you did not expect, when the config declares it.
agent-reference validateand the confidence a checkout reports are the tools for that. - Anything requiring an attacker who can already write to your
agent-reference.json, your lockfile, or your store directory. - Denial of service from a deliberately enormous repository. Clones are
--filter=blob:none, but a checkout is still as large as upstream made it.