Miri's cached environment crossed the pull request trust boundary
Miri persisted environment variables in target output that pull request jobs could restore from cache. The disclosure explains why preventing cache writes does not protect the confidentiality of cached data.
A pull request job does not need direct access to a secret if an earlier trusted job has saved that secret in a readable cache. The Rust Security Response Team's September 21 disclosure describes that path through Miri. cargo miri persisted environment variables in target/, and GitHub Actions workflows could then preserve those files for later pull request runs.
This is a confidentiality failure in the relationship between build output and cache readers. A cache policy that prevents an untrusted job from writing into a trusted cache can still allow it to read the cache. Those permissions protect different properties. The disclosed setup needed protection against sensitive data entering a cache that less trusted code could restore.
I value Rust's semantics because they make many low-level obligations explicit. CI introduces another set of obligations outside the compiled program. A tool that faithfully records its execution environment can create an unsafe workflow when the environment contains credentials and its output has a broader audience. The useful question is where the data goes after the process finishes.
Miri retained more environment than it needed
Miri needs to carry build-relevant environment information between invocations. The source revision linked by the disclosure defines CrateRunInfo, containing arguments and environment pairs as well as the current directory and optional standard input. Its collection path gathers the whole environment through env::vars_os().collect().
The same implementation serializes that information as JSON to a file. This makes the persistence mechanism concrete. A variable does not need to be printed in a job log to become durable output. Serializing a process environment into build metadata is another way to move its contents out of the process.
That behavior is understandable as an implementation shortcut. Capturing everything avoids losing a variable that a later invocation might need. Its cost is that unrelated variables receive the same lifetime as required build state. When a CI workflow injects credentials broadly, the shortcut also records data that has no legitimate reason to survive in an artifact intended for reuse.
The correction selects a smaller set of names
The Miri fix replaces the complete environment capture with a filter. It retains OUT_DIR and names beginning with CARGO_, excluding names ending with _TOKEN. The filter also requires the variable name to be representable as a string before applying those checks. The change narrows stored state to the family of variables Cargo is expected to set for this use.
That is a name-based selection rule, not a detector for every possible secret value. A caller can still place sensitive information under a name that a tool preserves. The disclosure explicitly says Cargo, Miri, and Rust do not guarantee that environment variables will never enter target/. Other build-time code can also persist its environment.
The fix therefore repairs this specific overbroad capture while preserving the workflow author's responsibility to scope credentials. Those two measures support each other. A tool should retain less unrelated data, and a workflow should avoid giving a process credentials that its work does not require.
As of the disclosure, the team says the corrected Miri release is expected in the September 22 nightly. That is an announced upcoming release, not evidence that every installed nightly already contains the patch. Existing saved output also has its own history, independent of the version used for the next invocation.
Cache read access is the relevant permission
GitHub's dependency-cache reference describes access across branch scopes. Pull request workflows can restore caches from their base branch, including pull requests from forks. That allows ordinary dependency reuse, but it also gives cached bytes to the code executing in the pull request workflow.
For this failure, cache poisoning is not required. The trusted job itself produces the sensitive file and saves it. The untrusted reader then uses an intended restore path. Restricting which jobs can write cache entries does not remove those bytes from the trusted entry or change who can restore them.
This distinction is easy to lose when all cache security discussion centers on malicious writes. Integrity asks whether the consuming build can trust restored content. Confidentiality asks whether every permitted reader may see that content. A cache can satisfy the first expectation and fail the second because a legitimate producer copied a credential into it.
The cache action's versioned input documentation also defines prefix-based restore-keys. A cache lookup can therefore have fallback matches beyond its primary exact key. A workflow analysis needs the complete restore configuration to identify possible readers and restored entries. Changing one primary key does not by itself describe what broader fallback prefixes still permit.
Exposure requires a complete data path
The Rust disclosure identifies the relevant combination of behavior. Miri must run with a secret in its environment, its target/ output must enter a cache, and a pull request workflow must be able to access that cache. Each part contributes a necessary step in the described disclosure path.
The environment may be populated by a workflow-level setting, a setting on the Miri step, or persistence from an earlier step. This makes the effective process environment more relevant than the visual location of the secret reference. A workflow can appear to use a credential in a separate step while still leaving it available to later processes.
The data path also explains why successful tests do not establish safety. Every assertion can pass while the tool's metadata carries unrelated credentials. That output is a side effect of preparing execution state. It does not require a failure in the Rust program under interpretation.
For a workflow that does not cache target/, this particular cache route is absent. For a workflow that does not expose credentials to Miri, the reported capture has no such credential to copy. Those distinctions support an exposure assessment without labeling every use of Miri as a leak.
Timing matters because the writer and reader can be different workflow runs. A trusted run may finish before the pull request exists. Its environment is no longer active on a runner, but the serialized file remains available through the saved entry. A later change to secret scope fixes the current workflow without rewriting the older entry. The exposure assessment must therefore connect the configuration used when an entry was created to the access available while that entry remained restorable. Looking only at the current workflow file can miss the earlier producer. This is a consequence of the described persistence path, rather than evidence that any particular repository has suffered unauthorized access.
Recovery includes saved entries
The disclosure recommends stopping the data path, clearing affected caches, and considering rotation of potentially exposed credentials. Disabling the cache for the job or restricting secrets to steps that do not invoke Miri are immediate ways to remove parts of the path. Temporarily disabling Miri is another option the team lists.
GitHub's cache-management documentation provides a way to inspect entries and delete them. The web interface lists creation and last-use times and supports filtering by branch or key. It also points to the CLI and REST API for management. Those records help identify the stored entries that belong to an affected workflow configuration.
Deletion and rotation answer different recovery questions. Removing an entry prevents another restore of that saved content through the cache. It cannot establish that no previous reader copied the content elsewhere. A credential's subsequent validity therefore needs its own decision, based on what the workflow exposed and which runs could access the saved output.
That decision should not require printing credentials during an audit. The workflow's variable names and scope, the tool's capture behavior, and cache accessibility establish the relevant path. Inspecting whether a known metadata file exists can be separated from displaying its sensitive contents. This preserves the evidence needed for remediation without creating another disclosure in the investigation log.
My preference is to treat a shared build cache as output distributed to every permitted reader. Under that model, a credential-bearing step needs an explicit reason to contribute files to it. The Miri fix reduces one source of accidental persistence, but the same review applies to build scripts and other tools that receive the environment and can write into the cached directory.