Protect shell snapshots when credential brokerage is enabled (#43909)

## Why

Shell startup files can introduce credentials or overwrite brokered dummy values. Snapshot capture and replay need to respect the command's sandbox and environment policy without persisting real credentials.

## What changed

- Capture and validate brokered snapshots per command using its requested shell, working directory, login mode, sandbox, and environment policy.
- Discover shell-initialized credentials and prepare snapshot exports for brokered replay. Preserve shell functions and aliases while preventing startup files from overwriting dummy credentials during replay.
- Rebuild snapshots as credential broker state changes and retain each command's snapshot for the lifetime of its process.
- Reject brokered POSIX shell execution when a protected snapshot cannot be created, and reject brokerage with `shell_zsh_fork`. Restore real credentials and remove brokerage markers for unsupported PowerShell and Cmd execution; escalated commands bypass protected snapshots.

## Testing

Add regression coverage for credential discovery and redaction, environment filtering, readonly exports, shell function and heredoc replay, command working directories and login modes, sandbox enforcement, and missing or disabled snapshots.

GitOrigin-RevId: 685356cb9e80f11cf69d053e4d239ee6ccf43a9e
This commit is contained in:
Winston Howes
2026-09-08 20:45:42 +00:00
committed by copyberry
parent 1530f828cb
commit 2e220af1f6
25 changed files with 2080 additions and 133 deletions

View File

@@ -57,6 +57,9 @@ impl Value {
pub(super) fn render(state: &str, aliases: &str, exports: &[Export<'_>]) -> Option<String> {
let mut output = format!("{state}{aliases}");
if !exports.is_empty() {
output.push_str("# exports (native declarations)\n");
}
for export in exports {
match export {
Export::Captured(source) => output.push_str(source),

View File

@@ -553,7 +553,7 @@ fn brokered_snapshot_alias_values_follow_allowed_credential_overrides() {
);
let expected = match expected {
Some((value, assignment)) => (
format!("# Snapshot file\n{assignment}\n"),
format!("# Snapshot file\n# exports (native declarations)\n{assignment}\n"),
HashMap::from([("AUTH_HEADER".to_string(), value)]),
Vec::new(),
),