{"record":{"id":"f4e1617495b222f0","repo":"astrid-runtime/astrid","slug":"process-path-overlaps-a-copy-on-write-mask","errorCode":null,"errorMessage":"process path {} overlaps a copy-on-write mask","messagePattern":"process path (.+?) overlaps a copy-on-write mask","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-workspace/src/sandbox/mod.rs","lineNumber":307,"sourceCode":"        for masked in extra_masks {\n            let _ = validate_sandbox_str(masked, \"workspace CoW mask\")?;\n            if !masked.exists() {\n                return Err(io::Error::new(\n                    io::ErrorKind::NotFound,\n                    format!(\n                        \"workspace CoW mask path does not exist: {} — refusing to spawn \\\n                         a child without the intended copy-on-write deny\",\n                        masked.display()\n                    ),\n                ));\n            }\n        }\n        for granted in extra_read_paths.iter().chain(extra_write_paths) {\n            if extra_masks\n                .iter()\n                .any(|masked| paths_overlap(granted, masked))\n            {\n                return Err(io::Error::new(\n                    io::ErrorKind::PermissionDenied,\n                    format!(\n                        \"process path {} overlaps a copy-on-write mask\",\n                        granted.display()\n                    ),\n                ));\n            }\n        }\n\n        #[cfg(target_os = \"linux\")]\n        {\n            // Bubblewrap implementation - paths are passed as separate argv entries (no injection).\n            // The process can only read the root OS, but can only write to the worktree and /tmp.\n            let mut bwrap = Command::new(\"bwrap\");\n            if clear_env {\n                bwrap.env_clear();\n            }\n            bwrap","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-workspace/src/sandbox/mod.rs#L289-L325","documentation":"After computing the process paths, wrap_with_process_paths rejects any granted read or write path that overlaps a copy-on-write mask. Overlap would let the child read or write through a path the mask is supposed to deny, defeating the CoW guarantee, so the call fails with PermissionDenied before spawning.","triggerScenarios":"Calling wrap_with_process_paths where an entry in extra_read_paths or extra_write_paths shares a path prefix (per paths_overlap) with any entry in extra_masks.","commonSituations":"Granting the workspace root as writable while also masking a subdirectory of it; copy-pasted path lists where a grant and a mask point at the same directory; symlink or relative-path aliases that resolve to the same target.","solutions":["Remove or narrow the granted path so it no longer overlaps the mask (grant a sibling/subpath instead)","Remove the conflicting mask if the path is legitimately meant to be writable","Normalize both paths (canonicalize) and compare to spot overlap from symlinks or relative components","Split the operation: run the masked child and the granted child as separate spawns"],"exampleFix":"// before\nwrite_paths.push(PathBuf::from(\"/workspace\")); // overlaps mask /workspace/secrets\n// after\nwrite_paths.push(PathBuf::from(\"/workspace/public\"));","handlingStrategy":"validation","validationCode":"fn overlaps(a: &Path, b: &Path) -> bool { a.starts_with(b) || b.starts_with(a) }\nassert!(grants.iter().all(|g| !masks.iter().any(|m| overlaps(g, m))),\n    \"grant overlaps a CoW mask\");","typeGuard":"fn grant_safe(grant: &Path, masks: &[PathBuf]) -> bool {\n    !masks.iter().any(|m| paths_overlap(grant, m))\n}","tryCatchPattern":"match wrap_with_process_paths(...) {\n    Err(e) if e.kind() == io::ErrorKind::PermissionDenied\n        && e.to_string().contains(\"overlaps a copy-on-write mask\") =>\n        eprintln!(\"narrow grant: {e}\"),\n    other => other?,\n}","preventionTips":["Canonicalize both grants and masks before comparing","Keep grant and mask path lists in one reviewed config section","Add a preflight check that reports the exact overlapping pair"],"tags":["sandbox","security","path-overlap","permission"],"backgroundTag":"permission-denied","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"}