release: v0.7.2 — Security Audit Remediation (SA-01 bis SA-07)
Sanctum Release / Build & Test (Windows x86_64) (push) Waiting to run
Sanctum Release / Sign & Release (push) Blocked by required conditions

- SA-01: Container-DoS / KDF-Amplification Schutz mit Pre-KDF Validierung, max 2 Slots (nur 0 und 1), Slot 0 Pflicht und strikten BLOB-Laengen
- SA-02: Release-Signierung in CI entkoppelt (getrennte build und sign-and-release Jobs, Secret-Isolation)
- SA-03: Pinned Download-Integritaet fuer minisign.exe in CI via SHA-256
- SA-04: Immutable Action-Pinning (@sha) und Toolchain-Pinning (1.85.0) in CI
- SA-05: Session-Token vollstaendig aus URIs verbannt (403 Forbidden bei Vorkommen im Pfad/Query)
- SA-06: Constant-Time Token- und Auth-Vergleiche via subtle::ConstantTimeEq
- SA-07: Dokumentations-Klarstellung bzgl. logischem Shredding vs. physischer SSD/FTL/CoW-Persistenz
This commit is contained in:
2026-09-18 23:40:35 +02:00
parent 1cdb30147b
commit fba7f305e3
28 changed files with 3048 additions and 968 deletions
+57 -16
View File
@@ -1,10 +1,10 @@
use std::path::PathBuf;
use sanctum::crypto::{
derive_kek, generate_dek, generate_salt, wrap_dek, KdfParams, MIN_MEMORY_COST_KIB,
MIN_TIME_COST,
};
use sanctum::mount::is_loopback_host;
use sanctum::storage::Database;
use std::path::PathBuf;
#[test]
fn test_is_loopback_host_comprehensive() {
@@ -45,7 +45,8 @@ fn test_is_loopback_host_comprehensive() {
#[test]
fn test_wal_and_shm_cleanup_on_close() {
let temp_dir = std::env::temp_dir();
let container_path: PathBuf = temp_dir.join(format!("test_wal_cleanup_{}.sanctum", std::process::id()));
let container_path: PathBuf =
temp_dir.join(format!("test_wal_cleanup_{}.sanctum", std::process::id()));
if container_path.exists() {
let _ = std::fs::remove_file(&container_path);
}
@@ -61,8 +62,9 @@ fn test_wal_and_shm_cleanup_on_close() {
let (wrapped_dek, nonce, tag) = wrap_dek(&kek, &dek).unwrap();
let db = Database::open(&container_path).unwrap();
db.init_schema(&salt, &kdf_params, &wrapped_dek, &nonce, &tag).unwrap();
db.init_schema(&salt, &kdf_params, &wrapped_dek, &nonce, &tag)
.unwrap();
// Erstelle Knoten, um Schreibaktivität im WAL zu erzeugen
let _ = db.create_node(1, "test_file.txt", false).unwrap();
db.checkpoint().unwrap();
@@ -77,24 +79,33 @@ fn test_wal_and_shm_cleanup_on_close() {
shm.push("-shm");
let _ = std::fs::remove_file(&shm);
assert!(!PathBuf::from(wal).exists(), "WAL-Datei darf nach sauberem Unmount nicht zurückbleiben");
assert!(!PathBuf::from(shm).exists(), "SHM-Datei darf nach sauberem Unmount nicht zurückbleiben");
assert!(
!PathBuf::from(wal).exists(),
"WAL-Datei darf nach sauberem Unmount nicht zurückbleiben"
);
assert!(
!PathBuf::from(shm).exists(),
"SHM-Datei darf nach sauberem Unmount nicht zurückbleiben"
);
let _ = std::fs::remove_file(&container_path);
}
#[test]
fn test_mount_security_multi_auth_and_no_token_in_url() {
use sanctum::mount::{check_basic_auth, check_token_header, strip_path_prefix};
use sanctum::mount::{check_basic_auth, check_token_header, uri_contains_token};
let session_token = "4f8a12bc90de45f187a23456789abcde";
let port = 8443;
let remote_url = format!("http://127.0.0.1:{}/", port);
// R-05: Die Remote-URL für Mount-Befehle darf niemals das Session-Token enthalten!
assert!(!remote_url.contains(session_token), "Remote URL darf niemals das Session-Token enthalten");
// R-05 & SA-05: Die Remote-URL für Mount-Befehle darf niemals das Session-Token enthalten!
assert!(
!remote_url.contains(session_token),
"Remote URL darf niemals das Session-Token enthalten"
);
// 1. Basic Auth Prüfung
// 1. Basic Auth Prüfung (Constant-Time)
use base64::Engine;
let auth_header = format!(
"Basic {}",
@@ -102,14 +113,44 @@ fn test_mount_security_multi_auth_and_no_token_in_url() {
);
assert!(check_basic_auth(&auth_header, session_token));
// 2. Token Header Prüfung
let wrong_auth = format!(
"Basic {}",
base64::engine::general_purpose::STANDARD.encode("sanctum:wrong_token_1234567890abcdef")
);
assert!(!check_basic_auth(&wrong_auth, session_token));
// 2. Token Header Prüfung (Constant-Time)
let mut headers = hyper::HeaderMap::new();
headers.insert("X-Sanctum-Token", session_token.parse().unwrap());
assert!(check_token_header(&headers, session_token));
// 3. Path-Prefix Fallback
let prefix = format!("/{}", session_token);
let uri: hyper::Uri = format!("http://127.0.0.1:8443/{}/test.txt", session_token).parse().unwrap();
let stripped = strip_path_prefix(&uri, &prefix).expect("Strip prefix");
assert_eq!(stripped.path(), "/test.txt");
let mut wrong_headers = hyper::HeaderMap::new();
wrong_headers.insert(
"X-Sanctum-Token",
"wrong_token_1234567890abcdef".parse().unwrap(),
);
assert!(!check_token_header(&wrong_headers, session_token));
// 3. SA-05: Verifikation, dass Token in URI (Pfad oder Query) erkannt und strikt abgewiesen wird
let uri_path: hyper::Uri = format!("http://127.0.0.1:8443/{}/test.txt", session_token)
.parse()
.unwrap();
assert!(
uri_contains_token(&uri_path, session_token),
"Token im Pfad muss erkannt werden"
);
let uri_query: hyper::Uri = format!("http://127.0.0.1:8443/test.txt?token={}", session_token)
.parse()
.unwrap();
assert!(
uri_contains_token(&uri_query, session_token),
"Token im Query-String muss erkannt werden"
);
let uri_clean: hyper::Uri = "http://127.0.0.1:8443/test.txt".parse().unwrap();
assert!(
!uri_contains_token(&uri_clean, session_token),
"Saubere URI darf kein Token enthalten"
);
}