{"record":{"id":"5e345174502798f9","repo":"jdx/mise","slug":"expected-world-writable-ancestor-to-be-refused","errorCode":null,"errorMessage":"expected world-writable ancestor to be refused","messagePattern":"expected world-writable ancestor to be refused","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/tests.rs","lineNumber":6651,"sourceCode":"        appdir.join(\"Foo.app/Contents/MacOS/foo\"),\n    );\n    Ok(())\n}\n\n#[test]\nfn ensure_trusted_appdir_refuses_world_writable_ancestor() -> Result<()> {\n    // Regression guard for the CI failure: a world-writable ancestor (as\n    // `/tmp` is, mode 1777) must be refused, because any local user could\n    // substitute components beneath it. Real application directories are\n    // never world-writable.\n    let tmp = trusted_tempdir()?;\n    let base = tmp.path().canonicalize()?;\n    let shared = base.join(\"shared\");\n    file::create_dir_all(&shared)?;\n    let mode = std::fs::Permissions::from_mode(0o1777);\n    std::fs::set_permissions(&shared, mode)?;\n    let err = match ensure_trusted_appdir(&shared.join(\"Applications\")) {\n        Ok(_) => panic!(\"expected world-writable ancestor to be refused\"),\n        Err(err) => err.to_string(),\n    };\n    assert!(err.contains(\"untrusted directory\"), \"{err}\");\n    Ok(())\n}\n\n#[test]\nfn ensure_trusted_appdir_creates_missing_tail() -> Result<()> {\n    let tmp = trusted_tempdir()?;\n    let base = tmp.path().canonicalize()?;\n    let appdir = base.join(\"Applications\");\n    ensure_trusted_appdir(&appdir)?;\n    assert!(appdir.symlink_metadata()?.file_type().is_dir());\n    // Idempotent when the directory already exists.\n    ensure_trusted_appdir(&appdir)?;\n    Ok(())\n}\n","sourceCodeStart":6633,"sourceCodeEnd":6669,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/tests.rs#L6633-L6669","documentation":"Test panic in the cask security tests: ensure_trusted_appdir was given an Applications path whose ancestor directory is world-writable (mode 01777), and it returned Ok instead of refusing. The test expects an error whose message contains 'untrusted directory', because staging app content under a world-writable ancestor would let any local user tamper with the target.","triggerScenarios":"Calling ensure_trusted_appdir(path) where any ancestor directory of path is world-writable (sticky 0o1777) and the function fails to detect/reject it, returning Ok(()).","commonSituations":"Users on shared machines with /tmp-style shared directories in the appdir path; regression in the ancestor trust check (e.g. permissions checks dropped during refactor, or running as a user that bypasses the check).","solutions":["Run the test to confirm ensure_trusted_appdir succeeds when it must fail","In ensure_trusted_appdir, walk each ancestor and reject with an 'untrusted directory' error when the mode's group/other write bits are set (allowing only the sticky case the implementation intends to trust)","Ensure canonicalization of the base path doesn't mask ancestor permission checks","Re-run the test and confirm the error message contains 'untrusted directory'"],"exampleFix":"// before (regressed)\nif !dir.is_dir() { return Err(...); } // no permission check\n// after\nlet mode = std::fs::metadata(dir)?.permissions().mode();\nif mode & 0o022 != 0 {\n    return Err(anyhow!(\"untrusted directory {}: group/other writable\", dir.display()));\n}","handlingStrategy":"validation","validationCode":"// caller-side guard before staging into an appdir\nfn ancestor_modes_ok(path: &Path) -> std::io::Result<bool> {\n    for anc in path.ancestors().skip(1) {\n        let mode = std::fs::metadata(anc)?.permissions().mode();\n        if mode & 0o022 != 0 { return Ok(false); } // group/other writable: untrusted\n    }\n    Ok(true)\n}\nassert!(ancestor_modes_ok(&shared.join(\"Applications\"))?);","typeGuard":"fn is_trusted_dir(p: &Path) -> bool {\n    std::fs::metadata(p).map(|m| m.is_dir() && m.permissions().mode() & 0o022 == 0).unwrap_or(false)\n}","tryCatchPattern":"match ensure_trusted_appdir(&appdir) {\n    Ok(()) => { /* proceed */ }\n    Err(e) if e.to_string().contains(\"untrusted directory\") => {\n        eprintln!(\"refusing to stage into world-writable ancestor: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Check every ancestor's permission bits, not just the leaf directory","Canonicalize paths before checking so symlinked ancestors are evaluated","Fail closed: refuse rather than warn when trust checks cannot run"],"tags":["rust","security","permissions","symlink","test-assertion"],"backgroundTag":"insufficient-permissions","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}