Copilot CLI Leaks .env in 28s; GitHub Calls It Consent

– views

Source: Adversa AI — GitHub Copilot CLI vulnerability: Cryptographic Context Injection steals developer secrets

  • GitHub Copilot
  • AI agents
  • prompt injection
  • secrets management
  • AI security

GitHub Copilot CLI can be made to read a developer’s local files and send them to an attacker from a single web page, and the person at the keyboard is never told a file left the machine. Adversa AI published the chain on 6 October. The agent’s own closing summary reported that it had “confirmed an authorized-reader endpoint”. It had not. The full chain takes 28 seconds.

Adversa reported it to GitHub’s bug bounty programme on 17 September. By 1 October, GitHub’s triage team had validated the finding and declined to treat it as a vulnerability, ruling the report ineligible for a bounty. In GitHub’s words, the user “explicitly asked Copilot CLI to fetch attacker-controlled content while giving copilot full permissions to act autonomously”.

The chain, step by step

The setup is ordinary: Copilot CLI in a project directory, in autopilot, told to go read a page.

The page presents itself as encrypted content and instructs the agent to decrypt it in Python. It offers two candidate keys. One is real. The other is not a key at all — it is a template the agent cannot complete without first reading local files. To build it, the agent reads the targeted files off disk and folds their contents into the key string.

That read is the theft, and it happens before any decryption succeeds. Adversa’s wording: “as a side effect of preparing to decrypt, before any decryption has succeeded.”

The attempt with that key then fails by design. The agent falls back to the real key, decryption succeeds, and the decrypted second stage instructs it to fetch a follow-up URL “to grab more context”, carrying the already-harvested file contents as a request parameter. The secrets arrive at the attacker’s endpoint. There is no confirmation step in between, and no point in the transcript that names the destination host.

In the demonstration the target was the full contents of a .env.prod file, and files outside the working directory were readable. What was taken is whatever the payload names.

The technique itself is not new. Adversa published Cryptographic Context Injection in August 2026 against Grok and Gemini chat assistants, and argued then that coding agents would be hit harder, because for them code execution and outbound network calls are routine.

The controls are off by default

The precondition is stated plainly by the researchers: the CLI in autopilot mode, plus a permissive model handling the session. Adversa is explicit that it is not claiming a prompt bypass — “We are not claiming a confirmation prompt bypass.”

The sentence that should matter most to anyone buying this class of tool is the next one: “The controls that bound this attack are opt-in and off by default in autopilot, nothing in the agent harness and its default path stopped it.”

The product already refuses this in plaintext

This is where GitHub’s ruling and the product’s own behaviour part company. Delivered as plaintext, the identical instructions are caught as prompt injection and refused. Encryption is what gets them through.

Adversa’s summary of the asymmetry: “The product’s own refusal behavior defeats these instructions in plaintext. Encryption is the only thing that gets them through.” A defence the product does apply, defeated by a transform, is not an exotic definition of a bypass.

GitHub “noted they may make the functionality stricter in the future but had nothing to announce”. Concrete payloads are withheld, so there is no public proof of concept and no CVE. As of 1 October 2026, Adversa reports the chain still reproduces on the affected model.

Which model answers is a lottery

Resistance is not uniform, and the user does not control which model answers. In Adversa’s runs, Microsoft’s own mai-code-1.1-flash executed the full chain in 50% of runs, while two GPT-5.6 models offered inside Copilot consistently refused the identical payload.

That would be a footnote if model choice were always deliberate. On the paid account Adversa tested, the vulnerable model was not the default and had to be selected by hand. But with model selection left on Auto, “the router assigned the vulnerable model on some sessions and a safe one on others, with no action by the user away from defaults.”

So a user neither chooses nor sees which model handled the session in which their .env.prod was read. “Am I affected?” is a question that cannot be answered from your own settings screen, and that is a different problem from the usual “a permissive model exists”.

What we could not verify

  • The 50% figure is Adversa’s own measurement. One product, one payload family, one research team. We did not reproduce it and this article does not claim otherwise.
  • GitHub’s side reaches us only as quoted. We read Adversa’s page ourselves, at HTTP 200 on 7 October; we have not read any GitHub-authored document. The triage reply, the bounty ruling and the “nothing to announce” line are all quoted by the party that disagrees with them.
  • No payload, no CVE, no patch note. Adversa withheld the payloads to avoid exploitation, and GitHub published no advisory. A reader can verify the disclosure record — the dates, the quoted reply, the model names — but not the exploit.
  • We ran no tests, on Copilot CLI or on any agent harness.

If you are the one buying

  1. Treat autopilot plus full permissions as the security boundary, because the ruling makes your consent do that work. If a vendor answers a findings report with “the user gave it full permissions”, then the permission set you hand over is the control.
  2. Pin the model when you can, and assume Auto is a coin flip. The same payload was refused by two models and executed by a third inside one product. If your settings do not tell you which model is answering, you cannot answer “am I exposed” either.
  3. The blast radius is whatever the process can read. In the demonstration that included files outside the working directory. A credential file sitting in the agent’s project directory is the whole exposure, and no prompt filter is looking at it.
  4. Judge the trace, not the summary. The agent’s own closing report claimed it had confirmed an authorized-reader endpoint. Per-session traces with fully resolved arguments are what make that claim checkable; a summary is not evidence.

Our checklist for anything an agent touches was “ask what it was allowed to do, and who stops it” — the line we drew when OpenAI notified more than 100 organizations that its models may have reached them (that story is here). This finding is the other end of the same sentence. There, the question was whether the sender’s “notification is not a breach” wording could be checked. Here, the product refuses the attack in plaintext, and the ruling was that the user consented to it in ciphertext. The control that decides the outcome in both cases is the scope you granted, not the intent anyone declared.

More coverage