feat(vault): clipboard hardening — clear-on-lock, backend trait, OSC52, configurable interval#5
Merged
Merged
Conversation
…2, configurable interval
Close the secret-can-outlive-the-agent limitation tracked since the M5
copy slice, and deliver PRD §7.5 in its architecturally honest shape.
Clear-on-lock sweep: the agent remembers the last value it copied
(Zeroizing, dropped once a timer clears it), and lock() now sweeps it
off the clipboard — covering `vault lock`, Quit/stop-agent, idle-lock,
and a new SIGTERM handler (PRD §7.3: lock, sweep, remove the socket,
exit). The sweep keeps the only-if-still-ours rule, so a newer copy by
the user is never clobbered.
Backend trait (PRD §7.5): new vault-agent/src/clipboard.rs defines a
Backend trait (arboard is the system implementation) so tests inject a
fake clipboard and the sweep logic is exercised on headless CI, and a
config-selected backend can slot in later. Status gains a
serde-defaulted clipboard_backend field (surfaced in `vault status`);
old-agent frames still decode.
OSC52 fallback (client-side, by design): OSC52 escapes to the user's
terminal — something the detached agent cannot do — so it lives in the
TUI. When the agent declines with the new typed
Error::ClipboardUnavailable, the TUI fetches the value (id-targeted
Get, the one path where the secret crosses the local UDS, same as
`vault get`) and emits the OSC52 sequence itself, tmux DCS-passthrough
wrapped when $TMUX is set. The TUI runs its own 30s timed clear and
sweeps on quit; a generated password falls back without any Get.
Configurable interval: vault-agent --clipboard-clear-secs N (then
$VAULT_CLIPBOARD_CLEAR_SECS, then 30; 0 disables) sets the agent
default; Request::Copy/CopyText still override per call. The new
Response::Copied { clear_after_secs } reports the effective window, so
the TUI toast shows the agent's real interval instead of a hardcoded 30.
Tests: fake-backend units for sweep-on-lock, never-clobber-newer-copy,
and timer/sweep marker interplay; Status backend-name reporting;
interval-resolution precedence; OSC52 sequence/clear/tmux-wrapping
units; and the old-agent Status decode regression. Verified live:
SIGTERM sweep-and-exit, clipboard_backend in status output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the secret-can-outlive-the-agent limitation tracked since the M5 copy slice, and delivers PRD §7.5 in its architecturally honest shape (the OSC52 fallback can only be client-side).
Clear-on-lock sweep
Zeroizing, dropped once a timer clears it), andlock()now sweeps it off the clipboard.vault lock,Quit/stop-agent, idle-lock, and a new SIGTERM handler (PRD §7.3: lock → sweep → remove socket → exit).Backend trait (PRD §7.5)
vault-agent/src/clipboard.rsdefines aBackendtrait;arboardis the one system implementation.Statusgains a serde-defaultedclipboard_backendfield (surfaced invault status, human +--json); old-agent frames still decode (regression-tested).OSC52 fallback — client-side, by design
Error::ClipboardUnavailable, the TUI fetches the value (id-targetedGet— the one path where the secret crosses the local UDS, same asvault get) and emits the OSC52 sequence itself, with tmux DCS-passthrough wrapping when$TMUXis set (set-clipboard onrequired).Getsince it's already local.Configurable interval
vault-agent --clipboard-clear-secs N→$VAULT_CLIPBOARD_CLEAR_SECS→ 30 (0disables);Request::Copy/CopyTextstill override per call.Response::Copied { clear_after_secs }reports the effective window, so the TUI toast shows the agent's real interval instead of a hardcoded 30.Tests
Fake-backend units (sweep-on-lock, never-clobber-newer-copy, timer/sweep-marker interplay),
Statusbackend-name reporting, interval-resolution precedence, OSC52 sequence/clear/tmux-wrapping units, and the old-agentStatusdecode regression. Workspace: 115 tests green,clippy -D warnings(pedantic+nursery) clean, both headless builds green,cargo denyclean. Verified live: SIGTERM sweep-and-exit,clipboard_backendin status output.🤖 Generated with Claude Code