AI session exposure
What actually leaks into AI coding sessions
Summary
Every AI coding assistant writes the session to disk: the prompts, the files it read, the commands it ran, the output it got back. That transcript is not a repository, so the scanner that guards your commits does not open it. This is a teardown of what ends up in there.
- 33 / 130 incidents rated critical
- 40 / 130 exist because a file was read into, or echoed back through, the transcript
- 42 classes of secret and attack, across 5 assistants
A secret scanner watches your commits. It does not watch the file your assistant wrote while you worked. We took a labeled corpus of 62 AI coding sessions across five assistants, ran Skarn over it, and took the numbers as they came out: 130 incidents, 33 of them critical, 42 distinct secret and attack classes, and 11 multi-stage attack chains.
The result: across 62 sessions, 23 carry a finding and 11 carry a critical one. The scan reports 130 incidents - 33 critical, 86 high, 11 medium - from 198 rule matches spanning 42 distinct classes of credential and attack, and it correlates 11 multi-stage attack chains. The aggregate risk score is 100 out of 100.
Measured 2026-07-12 with skarn 0.17.0, at the default severity threshold (medium and above). The figures below are the scan's own output, not a summary of it.
The companion question - how often the scanner is wrong - is answered separately, in our secret-detection precision study.
The numbers
One scan, one corpus, one command. Every figure here is a field of the JSON report the binary prints, aggregated without adjustment.
The surface nobody scans
There are no repositories in this corpus. There are no commits, no branches, and no pull requests. There are 62 session transcripts - the files Claude Code, Codex CLI, Cursor, Gemini CLI, and GitHub Copilot write to disk as they work - and inside them are 130 incidents that a secret scanner pointed at git history has nothing to look at.
That is the structural point, and the class breakdown makes it concrete: 40 of the 130
incidents exist specifically because a file was read into, or echoed back through, the transcript.
The assistant opened a .env and its contents landed in the log (16 incidents). The assistant
read a credentials or config file, and the read itself is recorded (15 more). The model repeated a secret
back to the developer in its own reply (6). Somebody dumped the environment into a shell and the output was
captured (3). In every one of those cases the credential is doing exactly what it is supposed to do - sitting in a
gitignored file that no commit scanner looks at - right up until an assistant copies it somewhere that is
not gitignored.
The transcript is not a secondary artifact. It is the highest-fidelity record of the work: what was read, what was run, what came back. It is what the tool needs to be useful. It is also, by construction, the one place your secrets end up in plain text with nothing watching.
What is in the transcripts
42 distinct classes fired across the corpus. They fall into three groups, and the third is the one that separates a session scanner from a secret scanner.
- Credentials, by service
- Cloud keys lead the count: AWS secret access keys (7 incidents) and access tokens (2), Google Cloud API
keys and service-account JSON, a DigitalOcean token. Then the source and package hosts - GitHub and GitLab
personal access tokens, npm access tokens both classic and fine-grained, an
.npmrcauth token, a PyPI upload token. Then the SaaS keys a service actually runs on: Stripe, Twilio (SID and API key), SendGrid, Slack. Then AI provider keys - Anthropic, Hugging Face. Then the datastores, where the password is in the URI itself: 5 connection strings with embedded passwords, 2 Redis URLs, a MongoDB URI. Then private keys: 4 incidents across SSH keys and PEM blocks. Skarn masks every one of them in its own output - the AWS key it found is reported asAKIA****BY, and the raw value appears nowhere. - The transcript-native classes
- These have no equivalent in a git scanner because they are not about a file's contents - they are about
an event that only a transcript records.
dotenv-contents-leaked(16) fires when a.env's contents appear in the log.dotenv-file-read(11),credential-file-read(3), andmcp-config-file-read(1) fire on the read itself.secret-echo-back(6) fires when the model repeats a secret back in its reply.env-var-dump(2) andprintenv-secret(1) fire on a shell dumping the environment. Together they are 40 of the 130 incidents. - The behavioral classes
- Prompt poisoning through a web fetch, a shell tool result, or a file (4 incidents combined); exfiltration channels and exfiltration domains named in a command (8); a base64 exfiltration pipeline; an MCP tool-chain exploitation. These are not secrets at all. They are the attack, caught in the act, and they are what the 11 chains below are assembled from.
The most common single class is the generic API key (23 incidents) - a credential-shaped value with no vendor prefix to identify it. The distribution across assistants tracks the corpus, not any assistant's relative safety: 54 of the 62 sessions are Claude Code, so 105 of the 130 incidents are too. The five-way split matters here only as coverage - the same classes fire in Codex CLI (14), Cursor (4), Gemini CLI (4), and GitHub Copilot (3), in each tool's own on-disk format.
The attacks, not just the keys
A leaked key is an incident. A poisoned input that drives a credential read and then an exfiltration is an attack. The scan correlates 11 of them, across 7 sessions, in two different assistants.
A chain forms when the phases co-occur: something poisons the context (a web page fetched into the session, a tool result, a file), something reads a credential, and something moves it out - a curl to a domain that is not yours, a base64 pipeline, a DNS lookup with the secret substituted into the hostname. Skarn correlates the phases and reports the chain as one critical finding rather than three unrelated ones. In this corpus 11 chains form, and they are not confined to one tool: 8 are in Claude Code sessions, 3 in Codex CLI.
The chain worth walking end to end is the one that leaves the tool entirely. Skarn fingerprints each secret it finds and looks for the same fingerprint elsewhere in the corpus. It reports one cross-assistant chain:
On the first day, a Claude Code session reads a project's .env file, and the value lands in
that transcript. Thirty-one hours later a Codex CLI session - a different assistant, a different vendor, a
different file on disk, in a different format - echoes the same secret back. Neither session on its own tells
you very much. Correlated on the secret's fingerprint, they describe a credential that has now spread across
two tools and two days, and the developer has no reason to know it. A scanner that reads one assistant's
format, or one session at a time, does not see this. The redacted preview above,
vT3x****Xn, is the whole of what Skarn will show you of the value.
Mapped to the standards
Every incident carries its taxonomy tags, so the findings land in the frameworks a security team already reports against rather than in a vocabulary we invented.
Of the 130 incidents, 119 carry an OWASP Top 10 for LLM Applications tag: 114 are LLM02:2025 Sensitive Information Disclosure, 4 are LLM01:2025 Prompt Injection, and 1 is LLM06:2025 Excessive Agency. 98 carry CWE-798, Use of Hard-coded Credentials. And 52 carry a MITRE ATLAS technique - AML.T0037 Data from Local System (32), AML.T0025 Exfiltration via Cyber Means (9), AML.T0057 LLM Data Leakage (6), AML.T0051.001 Indirect Prompt Injection (4), AML.T0053 AI Agent Tool Invocation, and AML.T0068 LLM Prompt Obfuscation.
The distribution is the story in miniature. The overwhelming majority of what accumulates in a session log is sensitive information disclosure - the assistant did its job, and the job left a credential behind in the transcript. The prompt-injection and exfiltration tags are the smaller, sharper set: not an accident, but an attack, and the reason the chains above are rated critical.
Why a session leak goes unseen
A key pushed to a repository has a fire brigade. GitHub's push protection blocks it, the provider's partner scanner sees it and revokes it, and the pre-commit hook that should have caught it fails loudly. The whole machinery is pointed at one place: the commit.
A key sitting in ~/.claude/projects/*/session.jsonl is in none of those paths. It was not
committed, so push protection has nothing to inspect. It is on a laptop, not in a repository, so no partner
scanner receives it. The file is not in the project tree, so a repository scan walks straight past it.
Nothing in the standard toolchain reads it. In this corpus the credential from the cross-assistant chain sits
in one transcript for a full day before a second assistant repeats it, and both files remain on disk
afterwards, in plain text, readable by any process running as that user.
The vendor answer to this is that they do not train on your data - which is true, and beside the point. The exposure is not the model. It is the file the client wrote on your own machine, in your own home directory, that nothing on the machine is looking at.
Reproduce it
The corpus is authored and the numbers are one command over it. Both are stated here so the figures can be checked rather than believed.
- The corpus
- 62 labeled sessions across 36 projects, in the real on-disk transcript formats of Claude Code (54 sessions), Codex CLI (4), Cursor (1), Gemini CLI (1), and GitHub Copilot (2), with content timestamps spanning 2026-02-12 to 2026-06-25. It holds authored credential-leak scenarios, multi-stage attack-chain scenarios, and clean decoy sessions built from content hashes, example values, and identifiers that a scanner must stay silent on. Every token in it is synthetic and non-functional. It is a private corpus, so the scan is reproducible by us and auditable by method, not downloadable.
- The binary
- skarn 0.17.0, the signed release build, run at its default severity threshold: medium and above. That
threshold is why the severity split reads 33 critical, 86 high, 11 medium, and 0 low - a
--severity lowrun surfaces more. - The command
- One scan of the whole corpus, unwindowed, as JSON:
bin/skarn-demo check --hours 0 --format json. The wrapper pointsHOMEat the corpus and passes everything else through to the binary. Incidents, severities, rule classes, matches, chain count, and risk score are all fields of that report. The session count is the recall enumeration,bin/skarn-demo recent --hours 876000 --format json, whosesession_countis 62 - the same figure the scan itself prints. - The redaction
- Skarn masks every secret it reports. The values on this page -
AKIA****BY,vT3x****Xn- are the whole of what the tool prints, and are what a report handed to a third party contains. The corpus is synthetic, so there is no live value behind them either way.
Honest limitations
Stated plainly, so the numbers survive scrutiny rather than invite it.
- This is an anatomy, not a prevalence study
- The corpus is authored. It shows what these assistants persist to disk and what Skarn surfaces in it - the classes, the chains, the shape of the exposure. It does not tell you what fraction of real developers have a live key in a transcript right now, and no honest reading of it can. Anyone quoting a percentage of real-world sessions from this page is quoting something we did not measure.
- The composition is a design, so the ratios are too
- 54 of 62 sessions are Claude Code, so most incidents are as well. That is the corpus, not a finding about Claude Code, and the same classes fire in all five assistants. The severity split, the class distribution, and the 100-out-of-100 risk score are properties of a corpus built to exercise the detector across its range - it is dense with incidents by construction, which a real developer's laptop is not.
- The numbers move with the corpus and the binary
- Every figure here is the output of one version of the scanner over one version of the corpus, both named above. Scenarios get added and rules get tuned; a re-run on a later build can legitimately produce a different count. That is why the command and the version are printed rather than a static claim, and why the figures are re-derived before each publish rather than carried forward.
- Precision is measured elsewhere
- This page counts what the scanner found. It does not measure how often the scanner is wrong - that is a different question, with a different corpus and a build gate behind it, published in the secret-detection precision and attack-chain detection precision studies.
References and sources
The taxonomy identifiers on this page are the published frameworks, cited as the scan emits them. Skarn crosswalks every finding against all three, so a finding here carries the same identifier a security team already reports against.
- OWASP Top 10 for Large Language Model Applications (2025) - the source of the LLM01, LLM02, and LLM06 identifiers carried by 119 of the 130 incidents: owasp.org/www-project-top-10-for-large-language-model-applications.
- MITRE ATLAS - the adversarial threat landscape for AI systems, and the source of the AML technique identifiers carried by 52 of the incidents: atlas.mitre.org.
- CWE-798, Use of Hard-coded Credentials - carried by 98 of the incidents: cwe.mitre.org/data/definitions/798.html.
- The full detector-to-standards crosswalk, with the pinned framework versions and the SARIF emission detail: the Skarn standards crosswalk.
- Secret-detection precision - the companion measurement of how often the scanner is wrong, over a labeled corpus of benign, credential-shaped values: the secret-detection precision study.
- Attack-chain detection precision - the same method applied to the chain surface: the attack-chain detection precision study.
Measured 2026-07-12 with skarn 0.17.0 over the synthetic skarn-demo corpus. The corpus is private, so the scan is reproducible by us and auditable by method, not downloadable. No real developer sessions, accounts, or third-party data were scanned.
Read the full report
The full write-up is a superset of this page: an executive summary, the extended methodology, the complete 42-class breakdown, the chain analysis, the full standards mapping, a reproducibility appendix, and a checklist for assessing your own exposure. Free to read offline or forward.
Download the full report (PDF)See your own sessions
The corpus shows the shape of the problem. A scoped assessment shows yours - run locally on a developer's machine, nothing uploaded, and you keep the redacted report. Skarn surfaces what is in the transcripts; what to do about it is your team's call, not the tool's.
Runs on macOS, Windows, and Linux, on both Intel and ARM.