Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b06094b2aa | ||
|
|
c9725f18d4 | ||
|
|
f62e74a3e9 | ||
|
|
1811ff4d33 | ||
|
|
3347efe418 | ||
|
|
e8f21f68a6 | ||
|
|
660e9cd5bf | ||
|
|
404aa91573 | ||
|
|
0ebf6ce97c |
@@ -0,0 +1,2 @@
|
|||||||
|
# Test vectors are compared byte for byte; never convert line endings.
|
||||||
|
tests/vectors/** -text
|
||||||
@@ -27,6 +27,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel, windows-2025]
|
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel, windows-2025]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
# The Go toolchain builds the test-only oracle; vault itself needs no Go.
|
# The Go toolchain builds the test-only oracle; vault itself needs no Go.
|
||||||
|
|||||||
Generated
+1
-1
@@ -1132,7 +1132,7 @@ checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vault"
|
name = "vault"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"argon2",
|
"argon2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vault"
|
name = "vault"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.99"
|
rust-version = "1.99"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|||||||
+2
-2
@@ -51,8 +51,8 @@ disables core dumps. See `SECURITY.md`.
|
|||||||
on Linux cross-builds macOS with `--no-default-features`: those binaries
|
on Linux cross-builds macOS with `--no-default-features`: those binaries
|
||||||
verify TLS for `add URL` and `mirror` against the bundled Mozilla roots
|
verify TLS for `add URL` and `mirror` against the bundled Mozilla roots
|
||||||
instead of the macOS keychain (so a corporate CA added to the keychain is
|
instead of the macOS keychain (so a corporate CA added to the keychain is
|
||||||
not trusted). Built on a Mac, or through Homebrew
|
not trusted). Built on a Mac (`cargo build --release`), vault uses the
|
||||||
(`brew install andydixon/tap/vault`), they use the keychain.
|
keychain.
|
||||||
|
|
||||||
## No longer released
|
## No longer released
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ $ chmod +x vault-linux-arm64 && mv vault-linux-arm64 $PREFIX/bin/vault
|
|||||||
|
|
||||||
Run \cmd{vault help}. You should see the full command list:
|
Run \cmd{vault help}. You should see the full command list:
|
||||||
|
|
||||||
\shot{help}{\cmd{vault help}: everything vault can do, on one screen}
|
\shot[0.95]{help}{\cmd{vault help}: everything vault can do, on one screen}
|
||||||
|
|
||||||
\section{Building it yourself}
|
\section{Building it yourself}
|
||||||
|
|
||||||
@@ -1332,6 +1332,17 @@ could not read passphrase (set VAULT\_PASSPHRASE\ldots) & No terminal to ask on,
|
|||||||
agent did not start: \ldots & \cmd{unlock} couldn't start its helper. The reason follows; commands still work, asking for the passphrase. \\
|
agent did not start: \ldots & \cmd{unlock} couldn't start its helper. The reason follows; commands still work, asking for the passphrase. \\
|
||||||
nothing was unlocked & \cmd{vault lock} found nothing to lock. Not an error. \\
|
nothing was unlocked & \cmd{vault lock} found nothing to lock. Not an error. \\
|
||||||
waiting for another vault process to finish & Not an error. Another command is writing; this one continues when it's done. \\
|
waiting for another vault process to finish & Not an error. Another command is writing; this one continues when it's done. \\
|
||||||
|
skipping unreadable record \ldots & A catalogue entry is damaged. Everything else still works; restore \file{records/} from backup to recover that file. \\
|
||||||
|
object does not match its catalogue record & The encrypted contents were swapped or damaged. Nothing was written. Restore \file{objects/} from backup. \\
|
||||||
|
object size does not match its catalogue record & Same as above: damage or tampering, caught before any output appeared. \\
|
||||||
|
cannot find home directory; set VAULT\_KEY & Unusual environments only. Set \env{VAULT\_KEY} and \env{VAULT\_DIR}. \\
|
||||||
|
unknown command / wrong number of arguments & A typo in the command itself. The usage text follows. \\
|
||||||
|
\end{vtable}
|
||||||
|
|
||||||
|
\subsection{Files, the lock and downloads}
|
||||||
|
|
||||||
|
\begin{vtable}{>{\ttfamily\footnotesize}p{58mm}Y}
|
||||||
|
\thead{MESSAGE} & \thead{WHAT IT MEANS, AND THE FIX} \\ \midrule
|
||||||
cannot lock the vault: \ldots & The lock file couldn't be opened or locked; the reason follows. Check the vault folder's permissions. \\
|
cannot lock the vault: \ldots & The lock file couldn't be opened or locked; the reason follows. Check the vault folder's permissions. \\
|
||||||
\ldots\ changed while being added & The file was being written while vault read it. Nothing was stored; try again when it's finished. \\
|
\ldots\ changed while being added & The file was being written while vault read it. Nothing was stored; try again when it's finished. \\
|
||||||
\ldots\ changed after it was stored; not deleting it & \cmd{migrate} stored the file, but it changed before it could be deleted. The original is kept; the vault has the version it read. \\
|
\ldots\ changed after it was stored; not deleting it & \cmd{migrate} stored the file, but it changed before it could be deleted. The original is kept; the vault has the version it read. \\
|
||||||
@@ -1342,11 +1353,6 @@ vault unlock is not supported on this platform\ldots & Windows: every command as
|
|||||||
download exceeds \ldots\ bytes & A download passed its size limit. Raise it with \cmd{--max-bytes} if you meant it. \\
|
download exceeds \ldots\ bytes & A download passed its size limit. Raise it with \cmd{--max-bytes} if you meant it. \\
|
||||||
redirected to another host: \ldots & \cmd{mirror} only follows redirects within the site it is copying. \\
|
redirected to another host: \ldots & \cmd{mirror} only follows redirects within the site it is copying. \\
|
||||||
mirror incomplete: stored \ldots, skipped \ldots & Some pages failed; the reasons were printed as they happened. Exit status 3. \\
|
mirror incomplete: stored \ldots, skipped \ldots & Some pages failed; the reasons were printed as they happened. Exit status 3. \\
|
||||||
skipping unreadable record \ldots & A catalogue entry is damaged. Everything else still works; restore \file{records/} from backup to recover that file. \\
|
|
||||||
object does not match its catalogue record & The encrypted contents were swapped or damaged. Nothing was written. Restore \file{objects/} from backup. \\
|
|
||||||
object size does not match its catalogue record & Same as above: damage or tampering, caught before any output appeared. \\
|
|
||||||
cannot find home directory; set VAULT\_KEY & Unusual environments only. Set \env{VAULT\_KEY} and \env{VAULT\_DIR}. \\
|
|
||||||
unknown command / wrong number of arguments & A typo in the command itself. The usage text follows. \\
|
|
||||||
\end{vtable}
|
\end{vtable}
|
||||||
|
|
||||||
\section{``My files have vanished!''}
|
\section{``My files have vanished!''}
|
||||||
|
|||||||
Generated
+1
-1
@@ -1091,7 +1091,7 @@ checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vault"
|
name = "vault"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+3
-2
@@ -526,12 +526,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(Instant::parse_rfc3339("2023-02-29T00:00:00Z"), None);
|
assert_eq!(Instant::parse_rfc3339("2023-02-29T00:00:00Z"), None);
|
||||||
assert!(Instant::parse_rfc3339("2024-02-29T00:00:00Z").is_some());
|
assert!(Instant::parse_rfc3339("2024-02-29T00:00:00Z").is_some());
|
||||||
let neg = Instant::from_system(std::time::UNIX_EPOCH - Duration::new(0, 1));
|
// 100 ns: Windows' SystemTime resolution.
|
||||||
|
let neg = Instant::from_system(std::time::UNIX_EPOCH - Duration::new(0, 100));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
neg,
|
neg,
|
||||||
Instant {
|
Instant {
|
||||||
secs: -1,
|
secs: -1,
|
||||||
nanos: 999_999_999
|
nanos: 999_999_900
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(Instant::from_system(neg.to_system()), neg);
|
assert_eq!(Instant::from_system(neg.to_system()), neg);
|
||||||
|
|||||||
+24
-22
@@ -24,7 +24,13 @@ struct Env {
|
|||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
fn new(tag: &str) -> Env {
|
fn new(tag: &str) -> Env {
|
||||||
let dir = std::env::temp_dir().join(format!(
|
// Not temp_dir(): macOS's long $TMPDIR pushes the agent socket path
|
||||||
|
// past SUN_LEN.
|
||||||
|
#[cfg(unix)]
|
||||||
|
let base = PathBuf::from("/tmp");
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let base = std::env::temp_dir();
|
||||||
|
let dir = base.join(format!(
|
||||||
"vault-cli-{tag}-{}-{}",
|
"vault-cli-{tag}-{}-{}",
|
||||||
std::process::id(),
|
std::process::id(),
|
||||||
std::time::SystemTime::now()
|
std::time::SystemTime::now()
|
||||||
@@ -48,6 +54,10 @@ impl Env {
|
|||||||
if let Some(p) = pass {
|
if let Some(p) = pass {
|
||||||
c.env("VAULT_PASSPHRASE", p);
|
c.env("VAULT_PASSPHRASE", p);
|
||||||
}
|
}
|
||||||
|
// No console, as Unix tests have no terminal: CI runners have a
|
||||||
|
// hidden one, where a prompt would wait forever.
|
||||||
|
#[cfg(windows)]
|
||||||
|
std::os::windows::process::CommandExt::creation_flags(&mut c, 0x0000_0008); // DETACHED_PROCESS
|
||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +162,7 @@ fn rust_lifecycle() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn go_made_rust_used() {
|
fn go_made_rust_used() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
lifecycle(go.to_str().unwrap(), RUST);
|
lifecycle(go.to_str().unwrap(), RUST);
|
||||||
}
|
}
|
||||||
@@ -160,7 +170,7 @@ fn go_made_rust_used() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn rust_made_go_used() {
|
fn rust_made_go_used() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
lifecycle(RUST, go.to_str().unwrap());
|
lifecycle(RUST, go.to_str().unwrap());
|
||||||
}
|
}
|
||||||
@@ -169,7 +179,7 @@ fn rust_made_go_used() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn boundary_sizes_interop() {
|
fn boundary_sizes_interop() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
let go = go.to_str().unwrap();
|
let go = go.to_str().unwrap();
|
||||||
let e = Env::new("bounds");
|
let e = Env::new("bounds");
|
||||||
@@ -382,7 +392,7 @@ fn lock_contention(holder: &str, waiter: &str) {
|
|||||||
#[test]
|
#[test]
|
||||||
fn lock_rust_waits_for_go() {
|
fn lock_rust_waits_for_go() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
lock_contention(go.to_str().unwrap(), RUST);
|
lock_contention(go.to_str().unwrap(), RUST);
|
||||||
}
|
}
|
||||||
@@ -391,7 +401,7 @@ fn lock_rust_waits_for_go() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn lock_go_waits_for_rust() {
|
fn lock_go_waits_for_rust() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
lock_contention(RUST, go.to_str().unwrap());
|
lock_contention(RUST, go.to_str().unwrap());
|
||||||
}
|
}
|
||||||
@@ -431,7 +441,7 @@ fn agent_rust() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn agent_go_client_rust_agent() {
|
fn agent_go_client_rust_agent() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
agent_roundtrip(RUST, go.to_str().unwrap());
|
agent_roundtrip(RUST, go.to_str().unwrap());
|
||||||
}
|
}
|
||||||
@@ -440,27 +450,19 @@ fn agent_go_client_rust_agent() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn agent_rust_client_go_agent() {
|
fn agent_rust_client_go_agent() {
|
||||||
let Some(go) = common::go_oracle() else {
|
let Some(go) = common::go_oracle() else {
|
||||||
return common::skip("go not installed");
|
return common::skip("no Go oracle");
|
||||||
};
|
};
|
||||||
agent_roundtrip(go.to_str().unwrap(), RUST);
|
agent_roundtrip(go.to_str().unwrap(), RUST);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn agent_pids(sock_dir: &Path) -> Vec<u32> {
|
fn agent_pids(sock_dir: &Path) -> Vec<u32> {
|
||||||
let mut pids = Vec::new();
|
// pgrep, not /proc: macOS has no /proc.
|
||||||
for e in std::fs::read_dir("/proc").into_iter().flatten().flatten() {
|
let o = Command::new("pgrep")
|
||||||
let Ok(pid) = e.file_name().to_string_lossy().parse::<u32>() else {
|
.args(["-f", &format!("__agent {}", sock_dir.display())])
|
||||||
continue;
|
.output()
|
||||||
};
|
.unwrap();
|
||||||
let Ok(cmd) = std::fs::read(e.path().join("cmdline")) else {
|
String::from_utf8_lossy(&o.stdout).lines().filter_map(|l| l.parse().ok()).collect()
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let cmd = String::from_utf8_lossy(&cmd);
|
|
||||||
if cmd.contains("__agent") && cmd.contains(&*sock_dir.to_string_lossy()) {
|
|
||||||
pids.push(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pids
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
|||||||
+6
-2
@@ -31,10 +31,14 @@ pub fn go_oracle() -> Option<PathBuf> {
|
|||||||
if let Some(p) = std::env::var_os("VAULT_GO_ORACLE") {
|
if let Some(p) = std::env::var_os("VAULT_GO_ORACLE") {
|
||||||
return Some(PathBuf::from(p));
|
return Some(PathBuf::from(p));
|
||||||
}
|
}
|
||||||
if !have_go() {
|
// The frozen Go version cannot sync directories on Windows ("Access
|
||||||
|
// is denied"), so its vaults cannot be shared there.
|
||||||
|
if cfg!(windows) || !have_go() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let out = root().join("target").join("vault-go-oracle");
|
let out = root()
|
||||||
|
.join("target")
|
||||||
|
.join(format!("vault-go-oracle{}", std::env::consts::EXE_SUFFIX));
|
||||||
let st = Command::new("go")
|
let st = Command::new("go")
|
||||||
.args(["build", "-o"])
|
.args(["build", "-o"])
|
||||||
.arg(&out)
|
.arg(&out)
|
||||||
|
|||||||
Reference in New Issue
Block a user