{"record":{"id":"5aa75bb6d8f40cfc","repo":"Hmbown/CodeWhale","slug":"owned-skill-path-escapes-direct-root","errorCode":null,"errorMessage":"owned skill path {} escapes direct root {}","messagePattern":"owned skill path (.+?) escapes direct root (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":318,"sourceCode":"    }\n    let exists = checked_real_directory(child)?;\n    if !exists {\n        if require_existing {\n            bail!(\"owned skill path {} does not exist\", child.display());\n        }\n        return Ok(false);\n    }\n\n    let canonical_root = fs::canonicalize(skills_dir).with_context(|| {\n        format!(\n            \"failed to resolve owned skill root {}\",\n            skills_dir.display()\n        )\n    })?;\n    let canonical_child = fs::canonicalize(child)\n        .with_context(|| format!(\"failed to resolve owned skill path {}\", child.display()))?;\n    if canonical_child.parent() != Some(canonical_root.as_path()) {\n        bail!(\n            \"owned skill path {} escapes direct root {}\",\n            child.display(),\n            skills_dir.display()\n        );\n    }\n    Ok(true)\n}\n\nfn validate_owned_skill_path(\n    ctx: &MutationContext<'_>,\n    skill: &AuditedSkill,\n    path: &Path,\n) -> Result<PathBuf> {\n    if skill.id.root_id != skill.root.id {\n        bail!(\"audited skill root identity changed; refusing mutation\");\n    }\n    let skills_dir = validate_owned_root_descriptor(ctx, &skill.root)?;\n    let package_name = on_disk_package_name(&skill.id)?;","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/skills/mutation.rs#L300-L336","documentation":"Defense-in-depth check in validate_owned_child: after canonicalizing both the skills root and the child, the child's real parent is no longer the real root. Some symlink inside the package directory redirects it outside the owned root, so mutating it would write elsewhere; the canonicalized child escaping the canonicalized root is the fault condition.","triggerScenarios":"Thrown at crates/tui/src/skills/mutation.rs:318 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Inspect the skill package directory for symlinks and remove them","Reinstall the skill so a clean, link-free package directory is created","Re-scan skills after repairing the directory"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}