{"record":{"id":"944186d3f5eea45a","repo":"zeroclaw-labs/zeroclaw","slug":"skill-security-audit-failed","errorCode":null,"errorMessage":"Skill security audit failed: {}","messagePattern":"Skill security audit failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/skills/mod.rs","lineNumber":2011,"sourceCode":"        _ => anyhow::bail!(\n            \"Unable to determine installed skill directory after clone (multiple new directories found)\"\n        ),\n    }\n}\n\nfn enforce_skill_security_audit(\n    skill_path: &Path,\n    allow_scripts: bool,\n) -> Result<audit::SkillAuditReport> {\n    let report = audit::audit_skill_directory_with_options(\n        skill_path,\n        audit::SkillAuditOptions { allow_scripts },\n    )?;\n    if report.is_clean() {\n        return Ok(report);\n    }\n\n    anyhow::bail!(\"Skill security audit failed: {}\", report.summary());\n}\n\nfn remove_git_metadata(skill_path: &Path) -> Result<()> {\n    let git_dir = skill_path.join(\".git\");\n    if git_dir.exists() {\n        std::fs::remove_dir_all(&git_dir)\n            .with_context(|| format!(\"failed to remove {}\", git_dir.display().to_string()))?;\n    }\n    Ok(())\n}\n\nfn copy_dir_recursive_secure(src: &Path, dest: &Path) -> Result<()> {\n    let src_meta = std::fs::symlink_metadata(src)\n        .with_context(|| format!(\"failed to read metadata for {}\", src.display().to_string()))?;\n    if src_meta.file_type().is_symlink() {\n        anyhow::bail!(\n            \"Refusing to copy symlinked skill source path: {}\",\n            src.display()","sourceCodeStart":1993,"sourceCodeEnd":2029,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/skills/mod.rs#L1993-L2029","documentation":"enforce_skill_security_audit runs audit_skill_directory_with_options over the skill directory and bails when the report has findings. Findings include: no SKILL.md (or deprecated manifest) at the skill root, symlinks anywhere inside, script-like files when allow_scripts is false (the default), markdown/toml files over 512 KiB, and forbidden-content findings in markdown/manifests. The message embeds report.summary(), so each finding names a file and a reason.","triggerScenarios":"install_local_skill_source or install_git_skill_source on a skill that ships shell/python helper scripts while config skills.allow_scripts is false; skill root lacking SKILL.md; any symlink inside the skill tree; SKILL.md larger than 512 KiB.","commonSituations":"Installing tool-style skills that carry .sh/.py helpers; copying a skill folder without its SKILL.md; vendored repos that symlink shared assets; generated skills with huge markdown.","solutions":["Read the findings in the message — each names a relative path and the reason it was flagged","If the finding is 'script-like files are blocked by skill security policy' and you trust the skill, set skills.allow_scripts = true in your zeroclaw config and reinstall","Add a SKILL.md at the skill root and remove symlinks (replace with real files)","Shrink oversized markdown/toml below the 512 KiB static-audit limit"],"exampleFix":"# before: install of a script-carrying skill fails\nzeroclaw skills install ./my-skill   # audit failed: scripts/setup.sh blocked\n\n# after: trust the skill explicitly\n# zeroclaw config:\n# [skills]\n# allow_scripts = true\nzeroclaw skills install ./my-skill","handlingStrategy":"validation","validationCode":"// Pre-audit with the library's public API before installing:\nuse zeroclaw_runtime::skills::audit::{audit_skill_directory_with_options, SkillAuditOptions};\nlet report = audit_skill_directory_with_options(\n    std::path::Path::new(source),\n    SkillAuditOptions { allow_scripts },\n)?;\nif !report.is_clean() {\n    eprintln!(\"would be rejected: {}\", report.summary());\n    // fix findings (remove scripts/symlinks, add SKILL.md) or set allow_scripts\n}","typeGuard":null,"tryCatchPattern":"match install_local_skill_source(source, &skills_path, allow_scripts) {\n    Err(e) if e.to_string().starts_with(\"Skill security audit failed\") => {\n        // parse the ';'-joined findings; scripts_blocked => hint allow_scripts=true\n    }\n    r => r,\n}","preventionTips":["Pre-audit skill directories with audit_skill_directory_with_options before install","Keep a SKILL.md at every skill root and avoid symlinks inside skills","Only enable skills.allow_scripts for skills you have reviewed"],"tags":["skills","security","audit","install","allow-scripts"],"backgroundTag":"security-scan-blocked","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}