release: v0.7.1 — Security-Patch (R-01 bis R-06)
Sanctum Release / Build & Release (Windows x86_64) (push) Canceled after 0s
Sanctum Release / Build & Release (Windows x86_64) (push) Canceled after 0s
- R-01: Bereinigung verbliebener Restbehauptungen in Doku und Code (LEGAL.md, README.md, QUICKSTART.md, main.rs, crypto/storage/recovery/verify.rs) - R-02: Lückenloser Carrier-Schutz in SanctumFs::copy (Quelle & Ziel) und sync (Pull-Skip & Push-Schutz) - R-03: Shared Dateinamen-Validierung (validate_node_name) in pathutil.rs, durchgesetzt in storage.rs und vfs.rs - R-04: Fail-closed Release-Packaging & obligatorische Minisign-Signatur in CI (.gitea/workflows/release.yaml) und Scripts - R-05: Session-Token Beseitigung im argv: In-Process Win32 WNetAddConnection2W/WNetCancelConnection2W, kein gio argv-Token, Multi-Auth HTTP Middleware (Basic Auth, X-Sanctum-Token, Path-Fallback) mit 401 WWW-Authenticate - R-06: Sofortiges Löschen von SANCTUM_RECOVERY_KEY aus der Prozessumgebung
This commit is contained in:
+9
-41
@@ -13,47 +13,7 @@ use crate::storage::{Database, NodeRecord};
|
||||
use crate::ui;
|
||||
use crate::vfs::is_leak_file;
|
||||
|
||||
/// Validiert Knotennamen gegen Path-Traversal (CWE-22) und reservierte Windows-Gerätenamen (S-08).
|
||||
pub fn validate_node_name(name: &str) -> Result<()> {
|
||||
if name.is_empty() {
|
||||
bail!("Dateiname darf nicht leer sein.");
|
||||
}
|
||||
if name == "." || name == ".." {
|
||||
bail!("Ungültiger Dateiname (Verzeichnisreferenz verboten): '{}'", name);
|
||||
}
|
||||
if name.contains('/') || name.contains('\\') || name.contains('\0') {
|
||||
bail!(
|
||||
"Dateiname enthält unzulässige Trennzeichen oder Null-Bytes: '{}'",
|
||||
name
|
||||
);
|
||||
}
|
||||
for c in name.chars() {
|
||||
if (c as u32) < 0x20 {
|
||||
bail!("Dateiname enthält Steuerzeichen: '{}'", name);
|
||||
}
|
||||
}
|
||||
|
||||
// Windows reservierte Gerätenamen (CON, PRN, AUX, NUL, COM1..9, LPT1..9)
|
||||
let base_name = if let Some(dot_idx) = name.find('.') {
|
||||
&name[..dot_idx]
|
||||
} else {
|
||||
name
|
||||
};
|
||||
let base_upper = base_name.to_ascii_uppercase();
|
||||
let reserved = [
|
||||
"CON", "PRN", "AUX", "NUL",
|
||||
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
||||
];
|
||||
if reserved.contains(&base_upper.as_str()) {
|
||||
bail!(
|
||||
"Dateiname '{}' kollidiert mit einem reservierten Windows-Gerätenamen.",
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub use crate::pathutil::validate_node_name;
|
||||
|
||||
/// Berechnet den SHA-256 Hash einer lokalen Datei für verlässliche Checksummen-Vergleiche (S-10).
|
||||
fn calc_local_file_sha256(path: &Path) -> Result<String> {
|
||||
@@ -288,6 +248,7 @@ pub fn sync_single_file_to_vault(
|
||||
let existing_node = children.into_iter().find(|c| c.name == file_name);
|
||||
|
||||
if let Some(ref node) = existing_node {
|
||||
db.assert_not_carrier(node.id)?;
|
||||
if node.is_dir {
|
||||
bail!("Pfad-Konflikt: '{}' existiert im Tresor als Ordner", file_name);
|
||||
}
|
||||
@@ -370,6 +331,7 @@ pub fn sync_single_file_to_host(
|
||||
checksum: bool,
|
||||
dry_run: bool,
|
||||
) -> Result<FileTransferResult> {
|
||||
db.assert_not_carrier(node.id)?;
|
||||
validate_node_name(&node.name)?;
|
||||
|
||||
if local_path.exists() {
|
||||
@@ -828,9 +790,15 @@ fn collect_and_pull_dir(
|
||||
stats: &mut SyncStats,
|
||||
vault_relative_paths: &mut HashSet<String>,
|
||||
) -> Result<()> {
|
||||
let carrier_id = db.find_carrier_node_id()?.unwrap_or(0);
|
||||
let children = db.list_children_in_vault(vault_node_id, vault_id, dek)?;
|
||||
|
||||
for child in children {
|
||||
// R-02 Carrier Guard: Trägerdatei niemals auf den Host spiegeln / herausziehen
|
||||
if carrier_id > 0 && (child.id == carrier_id || db.is_descendant_of(carrier_id, child.id).unwrap_or(false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
validate_node_name(&child.name)?;
|
||||
|
||||
let child_rel = if rel_prefix.is_empty() {
|
||||
|
||||
Reference in New Issue
Block a user