{"record":{"id":"f0c39136fd5eb55c","repo":"astrid-runtime/astrid","slug":"winfsp-service-mountpoint-is-malformed","errorCode":null,"errorMessage":"WinFsp service mountpoint is malformed","messagePattern":"WinFsp service mountpoint is malformed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-winfsp/src/win.rs","lineNumber":296,"sourceCode":"    let manifest_path = lease.resource_path.join(\"lease.json\");\n    platform_fs::validate_private_file(&manifest_path)\n        .context(\"validate private WinFsp lease manifest\")?;\n    let manifest = std::fs::read(&manifest_path).context(\"read WinFsp lease manifest\")?;\n    if manifest.len() > 64 * 1024 {\n        bail!(\"WinFsp lease manifest exceeds the bounded size\");\n    }\n    let admitted: StorageMountLeaseV1 =\n        serde_json::from_slice(&manifest).context(\"decode WinFsp lease manifest\")?;\n    if admitted != *lease {\n        bail!(\"WinFsp launch lease does not match the kernel manifest\");\n    }\n    if !launch.mountpoint.is_absolute()\n        || launch\n            .mountpoint\n            .components()\n            .any(|component| matches!(component, std::path::Component::ParentDir))\n    {\n        bail!(\"WinFsp service mountpoint is malformed\");\n    }\n    if is_public_mountpoint(&launch.mountpoint)\n        || launch.mountpoint.parent().is_none()\n        || launch.mountpoint == lease.resource_path\n        || launch.mountpoint.starts_with(&lease.resource_path)\n        || lease.resource_path.starts_with(&launch.mountpoint)\n    {\n        bail!(\"WinFsp service mountpoint is public or overlaps the lease resource\");\n    }\n    platform_fs::validate_private_directory(&launch.mountpoint)\n        .context(\"validate private WinFsp mountpoint\")?;\n    platform_fs::verify_no_redirects(&launch.mountpoint)\n        .context(\"reject redirected WinFsp mountpoint\")?;\n    if std::fs::read_dir(&launch.mountpoint)?.next().is_some() {\n        bail!(\"WinFsp service mountpoint is not empty\");\n    }\n    if !launch.control_path.is_absolute()\n        || launch","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-winfsp/src/win.rs#L278-L314","documentation":"The mountpoint where the WinFsp filesystem will be attached must be a safe, absolute path. This error fires when the mountpoint is relative or contains a ParentDir (\"..\") component — i.e., the path could resolve somewhere unintended. Because a filesystem mount is a privileged operation, path-traversal-shaped mountpoints are rejected outright.","triggerScenarios":"service_main -> validate_service_launch when !launch.mountpoint.is_absolute() or mountpoint.components() contains std::path::Component::ParentDir (e.g. \"..\\\\mount\", \"C:\\dir\\..\\mount\", \"mnt\\\\..\\\\x\").","commonSituations":"Config files specifying relative mount directories resolved against an unexpected CWD; user-supplied mountpoints concatenated with \"..\" to escape a base dir; templates that build the mountpoint by string formatting instead of PathBuf::join/canonicalize; Windows drive-relative paths like \"C:mount\" which are not fully absolute.","solutions":["Canonicalize the mountpoint in the launcher (fs::canonicalize / dunce::canonicalize) before embedding it in the launch descriptor.","Reject or sanitize user input: ensure the mountpoint is absolute and free of \"..\" components at configuration time.","Construct the mountpoint with PathBuf::join from a known root instead of string concatenation.","If traversal is legitimate, resolve it in the trusted launcher (base.join(user_rel) then canonicalize) and pass the resolved absolute path."],"exampleFix":"// before\nmountpoint: PathBuf::from(format!(\"{}\\\\..\\\\mounts\\\\{}\", base, id)),\n// after\nlet mp = base.join(\"mounts\").join(&id).canonicalize()?; // absolute, no ParentDir\nmountpoint: mp,","handlingStrategy":"validation","validationCode":"let mp = dunce::canonicalize(&mountpoint)?;\nif !mp.is_absolute() || mp.components().any(|c| matches!(c, std::path::Component::ParentDir)) {\n    return Err(anyhow!(\"mountpoint must be absolute with no '..' components\"));\n}","typeGuard":"fn safe_mountpoint(p: &std::path::Path) -> bool {\n    p.is_absolute()\n        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::CurDir))\n}","tryCatchPattern":null,"preventionTips":["Canonicalize mountpoints before embedding them in launch descriptors","Validate user-supplied mount paths at configuration time, not at service start","Build paths with PathBuf::join from a trusted root instead of string formatting","Add a fuzz/property test asserting the launcher never emits ParentDir components"],"tags":["winfsp","path-traversal","mountpoint","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}