fix(peer): apply catalog portability rules in validate_relative_path
Security audit finding EXP2-SEC-03. `path_validation.rs` guarded against traversal, UNC prefixes, drive letters and symlink escapes, but unlike the catalog validators in lanspread-db it did not reject Windows device names (CON, NUL, COM1..9, LPT1..9), components with a trailing dot or space, reserved characters (`<>:"|?*`), or control characters. On Windows, opening `NUL.txt` talks to a device and `file.txt.` is silently rewritten to `file.txt`, so such names must never reach the filesystem. The catalog component validator is now exported from lanspread-db as `validate_portable_component` and applied to every normal component in `validate_relative_path`. The only current caller is Stream Install's staging-path resolution, whose inputs are already canonical catalog paths, so this changes nothing for valid archives; it removes a divergence between two validators that are supposed to agree. Test plan: `just test` (new cases cover device names in any position, trailing dot/space, a reserved character and a control character, and confirm `console.txt` and `com10.txt` stay valid). Claude-Session: https://claude.ai/code/session_017C3Nbgwpdm3YNwZhhFLHwg
This commit is contained in:
3 files changed
+48
-1
No files matched your search
@@ -41,7 +41,7 @@ pub use model::{
|
||||
MAX_CATALOG_PATH_BYTES,
|
||||
MAX_CATALOG_TOTAL_BYTES,
|
||||
};
|
||||
pub use path::CanonicalCatalogPath;
|
||||
pub use path::{CanonicalCatalogPath, validate_portable_component};
|
||||
pub use store::{
|
||||
CATALOG_PUBLICATION_MARKER_NAME,
|
||||
CatalogManifestStore,
|
||||
|
||||
@@ -164,6 +164,17 @@ fn validate_path(path: &str) -> eyre::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates one path component under the portable catalog rules: no
|
||||
/// `.`/`..`, at most 255 bytes, no trailing dot or space, no control or
|
||||
/// Windows-reserved characters, and no Windows device name stem.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the first violated rule.
|
||||
pub fn validate_portable_component(component: &str) -> eyre::Result<()> {
|
||||
validate_component(component)
|
||||
}
|
||||
|
||||
fn validate_component(component: &str) -> eyre::Result<()> {
|
||||
if component.is_empty() || matches!(component, "." | "..") {
|
||||
eyre::bail!("catalog path contains a non-canonical component: {component:?}");
|
||||
|
||||
Reference in new issue
Block a user