envvault/cli/commands/
get.rs1use crate::cli::{load_keyfile, prompt_password_for_vault, vault_path, Cli};
4use crate::errors::{EnvVaultError, Result};
5use crate::vault::VaultStore;
6
7pub fn execute(cli: &Cli, key: &str, clipboard: bool) -> Result<()> {
9 let path = vault_path(cli)?;
10 let keyfile = load_keyfile(cli)?;
11
12 let vault_id = path.to_string_lossy();
14 let password = prompt_password_for_vault(Some(&vault_id))?;
15 let store = match VaultStore::open(&path, password.as_bytes(), keyfile.as_deref()) {
16 Ok(store) => store,
17 Err(e) => {
18 #[cfg(feature = "audit-log")]
19 crate::audit::log_auth_failure(cli, &e.to_string());
20 return Err(e);
21 }
22 };
23
24 let value = store.get_secret(key)?;
26
27 if clipboard {
28 copy_to_clipboard(&value)?;
29 crate::cli::output::success(&format!("Copied '{key}' to clipboard (clears in 30s)"));
30
31 spawn_clipboard_clear();
33 } else {
34 println!("{value}");
35 }
36
37 #[cfg(feature = "audit-log")]
38 crate::audit::log_read_audit(cli, "get", Some(key), None);
39
40 Ok(())
41}
42
43fn copy_to_clipboard(value: &str) -> Result<()> {
45 let mut clip = arboard::Clipboard::new()
46 .map_err(|e| EnvVaultError::ClipboardError(format!("failed to access clipboard: {e}")))?;
47 clip.set_text(value)
48 .map_err(|e| EnvVaultError::ClipboardError(format!("failed to copy to clipboard: {e}")))?;
49 Ok(())
50}
51
52#[cfg(unix)]
56fn spawn_clipboard_clear() {
57 use std::process::{Command, Stdio};
58
59 let clear_cmd = "sleep 30 && \
61 (printf '' | xclip -selection clipboard 2>/dev/null || \
62 xsel --clipboard --delete 2>/dev/null || \
63 printf '' | pbcopy 2>/dev/null || true)";
64
65 let result = Command::new("sh")
66 .args(["-c", clear_cmd])
67 .stdin(Stdio::null())
68 .stdout(Stdio::null())
69 .stderr(Stdio::null())
70 .spawn();
71
72 if result.is_err() {
73 crate::cli::output::warning("Could not schedule clipboard auto-clear");
74 }
75}
76
77#[cfg(not(unix))]
78fn spawn_clipboard_clear() {
79 crate::cli::output::warning(
80 "Clipboard auto-clear is not supported on this platform — clear manually",
81 );
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn clipboard_copy_returns_error_on_invalid_clipboard() {
90 let result = copy_to_clipboard("test-value");
93 if let Err(e) = result {
96 let msg = e.to_string();
97 assert!(msg.contains("clipboard") || msg.contains("Clipboard"));
98 }
99 }
100}