M4a task 16: read a secret file from the file that was checked (review finding 2)
The test swaps the file between the check and the open through a `between` hook. A reference fix passed it and the gate (769 tests) in the working tree, caught two mutations, and was removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
//! A secret file is read from the file that was checked, never from whatever the path names a
|
||||
//! moment later (M4a review, finding 2). `read_checked` runs `between` after checking the path and
|
||||
//! before opening it; each test swaps something there. Do not edit.
|
||||
|
||||
#[path = "support/tmp.rs"]
|
||||
mod tmp;
|
||||
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use gatewayd::secrets::read_checked;
|
||||
use tmp::TempDir;
|
||||
|
||||
const TOKEN: &str = "the-real-token";
|
||||
const OTHER: &str = "a-file-the-owner-never-chose";
|
||||
|
||||
fn owner_file(dir: &TempDir, name: &str, text: &str) -> PathBuf {
|
||||
let path = dir.write(name, text);
|
||||
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
fn refused(path: &Path, between: &dyn Fn(), word: &str) {
|
||||
let why = read_checked(path, between).expect_err(word);
|
||||
assert!(why.contains(word), "{word}: {why}");
|
||||
assert!(why.contains(&path.display().to_string()), "{why}");
|
||||
assert!(!why.contains(TOKEN) && !why.contains(OTHER), "never a value: {why}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_untouched_file_is_read() {
|
||||
let dir = TempDir::new("race-ok");
|
||||
let path = owner_file(&dir, "token", &format!("{TOKEN}\n"));
|
||||
let bytes = read_checked(&path, &|| {}).unwrap();
|
||||
assert_eq!(bytes.as_slice(), format!("{TOKEN}\n").as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_file_swapped_for_a_link_is_refused() {
|
||||
let dir = TempDir::new("race-link");
|
||||
let path = owner_file(&dir, "token", TOKEN);
|
||||
let other = owner_file(&dir, "other", OTHER);
|
||||
let swap = || {
|
||||
std::fs::remove_file(&path).unwrap();
|
||||
std::os::unix::fs::symlink(&other, &path).unwrap();
|
||||
};
|
||||
refused(&path, &swap, "changed while it was read");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_file_swapped_for_another_file_is_refused() {
|
||||
let dir = TempDir::new("race-rename");
|
||||
let path = owner_file(&dir, "token", TOKEN);
|
||||
let other = owner_file(&dir, "other", OTHER);
|
||||
let swap = || std::fs::rename(&other, &path).unwrap();
|
||||
refused(&path, &swap, "changed while it was read");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_checks_hold_for_the_file_that_is_read() {
|
||||
let dir = TempDir::new("race-mode");
|
||||
let path = owner_file(&dir, "token", TOKEN);
|
||||
let widen = || {
|
||||
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o644)).unwrap();
|
||||
};
|
||||
refused(&path, &widen, "has mode 644");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_path_checks_still_come_first() {
|
||||
let dir = TempDir::new("race-first");
|
||||
let other = owner_file(&dir, "other", OTHER);
|
||||
let link = dir.path().join("link");
|
||||
std::os::unix::fs::symlink(&other, &link).unwrap();
|
||||
refused(&link, &|| panic!("never reached for a link"), "is a symbolic link");
|
||||
refused(dir.path(), &|| panic!("never reached for a directory"), "is not a regular file");
|
||||
let relative = Path::new("relative/token");
|
||||
let why = read_checked(relative, &|| panic!("never reached")).unwrap_err();
|
||||
assert!(why.contains("is not an absolute path"), "{why}");
|
||||
}
|
||||
Reference in New Issue
Block a user