On 21 September 2026 the Rust Security Response Team warned that Miri, the interpreter Rust projects use to catch undefined behaviour in unsafe code, stores all environment variables under target/. On its own that is a quirk. Combined with GitHub Actions caching of target/, it means secrets present in a Miri job can be read later by pull requests that restore the same cache.
The team scanned public repositories and found one that was vulnerable, plus seven that did not appear vulnerable but were told to be careful. All eight maintainers were contacted. The post does not say any secret was actually stolen, but it is clear that a scan can miss cases, so every project running Miri in CI should check its own setup.
What's affected
The behaviour lives in cargo-miri (the post links to cargo-miri/src/util.rs at commit 165a9c3), which saves the whole environment into target/ so it can rebuild the right context later. No Miri version range is given because Miri ships with nightly toolchains.
You are likely affected if all four of these are true:
- A CI job runs
cargo miri. - That job has secrets in its environment. This includes secrets passed to the Miri step, secrets set in the workflow-level
envblock, and secrets passed to an earlier step that left them in the environment. - The workflow caches
target/, for example withactions/cacheorSwatinem/rust-cache. The post names these as common ways to cache the directory, not as vulnerable actions. - Pull request workflows can read that cache.
The attack path the post describes is short. A first pull request needs maintainer approval to run CI, but later pushes rerun it automatically. Someone who has landed a change before can run a CI job that reads the cached target/ directory, then push a second commit to hide what they did. Overwritten commits can be hard to see, and CI logs expire after a few months.
Fix: rust-lang/miri#5337 makes Miri preserve only CARGO_* variables (except CARGO_*_TOKEN) and OUT_DIR. The team said the Miri in the nightly of 22 September 2026 would no longer have the problem.
How to check if you're exposed
Start by finding workflows that run Miri and cache build output:
# Workflows that run Miri
grep -rln "cargo miri\|cargo +nightly miri" .github/workflows/
# Of those, which cache target/ or use a Rust cache action
grep -rlnE "actions/cache|rust-cache" .github/workflows/ | \
xargs grep -l "miri"
For each match, answer three questions:
- Are secrets in scope? Look for
secrets.references in the workflow-levelenv, the job-levelenv, and any step before the Miri step in the same job. - Is
target/in the cache path?Swatinem/rust-cachecachestarget/by default. Withactions/cache, read thepathlist. - Can pull requests restore the cache? GitHub's caching reference says a run can restore caches from its own branch and the default branch, and a pull request run can also restore caches from its base branch.
If you have the GitHub CLI, list what is cached right now:
gh cache list --limit 100
To see whether a cached directory already holds secrets, restore it in a throwaway clone and search. Search for the variable names, not the values, so nothing sensitive ends up in your terminal history:
grep -rlE "GITHUB_TOKEN|AWS_SECRET_ACCESS_KEY|CARGO_REGISTRY_TOKEN|NPM_TOKEN" target/ 2>/dev/null
What to do now
- Remove secrets from the Miri job. Scope secrets to the specific steps that need them, never to the workflow or job
env. The post notes that standardcargo buildandcargo testrarely need secrets. - Or stop caching for that job. Disabling the cache for the Miri job is one of the quick fixes the post lists. Temporarily disabling Miri is another.
- Update your nightly toolchain to one from 22 September 2026 or later so Miri stops writing the full environment.
- Clear existing caches. A fixed Miri does not clean entries already written. Delete them:
gh cache delete --all - Rotate secrets that were present in a Miri job with a cached
target/, starting with anything that can publish (crates.io tokens, cloud deploy keys). - Separate cache writers from secret holders. The post's broader advice: jobs that can write to caches pull requests can read should not have access to secrets.
Why it matters
The Rust team was careful to say this is not a bug in GitHub Actions or in the cache actions. Cargo, Miri and Rust make no promise that environment variables stay out of target/, and build scripts can write anything they like into build output. The real problem is a cache that is shared across trust levels and filled by a job that happened to hold secrets.
That pattern is not specific to Rust. Gradle caches, node_modules caches and Python virtual environments in CI can all pick up values from the environment of the job that built them. Any cache that a lower-trust workflow can restore is an output of the higher-trust job, so treat it like one.
Try it yourself
Pick one repository that runs Miri or any build with secrets in scope. Write down every cache key the workflow saves, which jobs write each key, and which events (push, pull_request, pull_request_target) can restore it. If any key is written by a job with secrets and readable by pull_request, you have found the same class of problem.
Click through it at your own pace, or open it full screen.
Quick check
Your Miri job has no secrets passed to it, but the workflow sets a deploy token in its top-level env block. Are you affected?
Possibly, yes. The post lists secrets set in the workflow's env as one way they end up in the Miri step's environment. If target/ is cached and pull requests can read the cache, treat it as exposed.
Does updating to a fixed nightly clean up secrets already in your cache?
No. The fix stops new writes. The post tells you to clear the cache after applying a fix and to consider rotating secrets that may have leaked.
What does the fixed Miri keep from the environment?
Only CARGO_* variables, excluding CARGO_*_TOKEN, plus OUT_DIR, per rust-lang/miri#5337.