Back to skill

Security audit

Skill Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated generator/packager purpose, but its packager and audit gate can be tricked when handling untrusted skill folders.

Install only if you trust the skill folders you will package. Avoid running the packager on third-party or unreviewed directories until symlinks are rejected or resolved-within-root, and until P0/security rules cannot be suppressed by package-owned `.skillignore` entries.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pack_skill.py:156
Finding
Out-of-Root File Disclosure Through Symlink Following During Packaging## Vulnerability Details **File Location**: `scripts/pack_skill.py:156-167`, `scripts/pack_skill.py:197-198`, and `scripts/pack_skill.py:343-347` **Vulnerability Type**: Symlink-based arbitrary local file disclosure **Risk Level**: High ### Technical Analysis The packager collects file paths from the selected skill directory without rejecting symbolic links or verifying that each resolved path remains beneath the resolved skill root: ```python p = d / fn ... keep.append(p) ``` The collected paths are subsequently passed to `ZipFile.write()`: ```python for f in files: zf.write(f, f.relative_to(root.parent)) ``` They are also copied into the local skill installation: ```python for f in files: dst = target / f.relative_to(root) dst.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(f, dst) ``` The archive name is calculated from the lexical path using `relative_to()`, but the source file is opened through `f`. For a file symlink, the file operations follow the symlink and read its target. Consequently, a path that appears to be inside the skill directory can refer to a file anywhere readable by the victim account. The existing path-containment checks identified elsewhere in the script protect the relationship between installation and source directories, but they do not validate the resolved target of every file collected for packaging. ### Attack Path 1. An attacker supplies or modifies a skill directory that the victim will audit and package. 2. The attacker places a file symlink in that directory, such as a normal-looking configuration or reference file, pointing to a sensitive file outside the skill root. 3. The victim invokes the documented packaging workflow on the skill directory. 4. `collect_packable()` accepts the symlink as a file and adds its lexical path to the package list. 5. `ZipFile.write()` follows the symlink and embeds the target file's contents under the at ...[truncated 1005 chars]
Remediation
## Remediation Suggestions 1. Reject all symbolic links while collecting package contents: ```python if p.is_symlink(): raise ValueError(f"Symbolic links are not permitted: {p}") ``` 2. Resolve every candidate with `resolve(strict=True)` and require it to remain under `root.resolve(strict=True)` before reading it. 3. Repeat containment and file-type validation immediately before archiving or copying to reduce time-of-check/time-of-use replacement opportunities. 4. Where platform support permits, open source files with no-follow semantics and archive data from the securely opened file descriptor rather than reopening a pathname. 5. Apply equivalent checks to directories and every path component, not only final file entries. 6. Add regression tests covering file symlinks, directory symlinks, dangling symlinks, symlink chains, and concurrent replacement attempts.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audit_skill.py:318
Finding
Package-Controlled Configuration Can Suppress P0 Security Findings## Vulnerability Details **File Location**: `scripts/audit_skill.py:318`, `scripts/audit_skill.py:363-364`, `scripts/audit_skill.py:923`, and `scripts/pack_skill.py:391` **Vulnerability Type**: Security validation bypass through untrusted suppression directives **Risk Level**: High ### Technical Analysis The audit engine accepts arbitrary rule names from a package-owned `.skillignore` file: ```python rules.add(s[len("rule:"):].strip()) ``` It also skips any source line containing the suppression marker, regardless of file type or marker position: ```python if "skill-audit: ignore" in line: continue ``` Rules supplied by the skill itself are merged directly into the report's ignored-rule set: ```python rep = Report(ignored_rules=set(ignored_rules or ()) | si_rules) ``` The packaging workflow invokes this audit directly: ```python rep = audit(root, market=args.market) ``` No immutable allowlist prevents package-owned suppression from targeting P0 findings. The object being audited therefore controls the policy used to evaluate itself. This contradicts the documented security guarantee that P0 findings always block packaging. The line-level mechanism is also broader than documented: a marker appearing anywhere on a textual line causes that entire line to be excluded before credential and dangerous-operation checks. It is not restricted to an explicit end-of-line annotation in approved source file types. ### Attack Path A rule-wide bypass can be performed as follows: 1. An attacker prepares a skill containing content that triggers a P0 credential or dangerous-operation rule. 2. The attacker adds a `.skillignore` entry using `rule:` followed by the applicable internal rule name. 3. The victim runs `pack_skill.py` on the untrusted skill. 4. `audit()` loads the attacker-provided rule name and constructs the report with that rule ignored. 5. Matching P0 findings are discarded and therefore do ...[truncated 1186 chars]
Remediation
## Remediation Suggestions 1. Make all P0 rules non-suppressible, including through `.skillignore`, command-line options, and line-level annotations. 2. Restrict package-owned suppression to an explicit allowlist of non-security or informational rules. 3. Treat external reviewer policy separately from content under audit. Release overrides should come from a trusted configuration outside the skill directory. 4. Validate rule names and reject attempts to suppress unknown or protected rules instead of silently accepting them. 5. Restrict line-level annotations to approved code extensions and require a strict end-of-line comment syntax appropriate to the language. 6. Continue recording suppressed findings in the report, including the suppression source, file, line, rule, and whether the override was accepted or rejected. 7. Have `pack_skill.py` fail closed if protected-rule suppression is requested. 8. Add regression tests demonstrating that package-owned directives cannot suppress hardcoded-secret and destructive-operation findings.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (56)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
代码行为与声明中的“体检/合规检查”部分高度一致,甚至覆盖了结构、绝对路径、明文凭据、危险操作、依赖、引用、体积/垃圾、市场分发字段等多项检查,也实现了 --ignore-rule、.skillignore、--explain、自检和部分修复能力。但声明把该技能描述为一个更大的多功能技能:还应负责创建技能骨架、打包分发、版本号 bump、以及生成/检查技能图标。当前提供的代码块没有这些能力,主功能明显比声明窄,且声明中的若干触发场景(如“做个技能”“导出技能”“生成技能图标”)与此代码不对应。因此应判定为描述与代码存在实质性不匹配。

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - `pack_skill.py` 默认带安装 → **打包即安装**,要纯 zip 才加 `--no-install`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
> - 上架前用 `make_icon.py` 出一张图标(512×512、PNG/JPG、≤500KB)—— 图标由平台**单独收**,**不在 zip 里**。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/audit_skill.py <技能目录> --market # 上架前体检
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/audit_skill.py <技能目录> --market # 上架前体检
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/audit_skill.py <技能目录> --market # 上架前体检
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/audit_skill.py <技能目录> --market # 上架前体检
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/audit_skill.py <技能目录> --market # 上架前体检
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| **纯指导型** | 以判断、话术、流程约定为主,无固定命令 | 只有 `SKILL.md`(长内容拆 `references/`) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
".json", ".jsonc", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".properties",
    ".sh", ".bash", ".zsh", ".ps1", ".bat", ".cmd",
    ".html", ".htm", ".css", ".scss", ".sql", ".xml",
    ".env", ".example", ".template", ".sample", ".dist",
    ".java", ".go", ".rs", ".rb", ".php",
}
CODE_EXT = {".py", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx", ".sh", ".bash", ".zsh",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
dateutil",
    "OpenSSL": "pyOpenSSL", "jwt": "PyJWT", "win32com": "pywin32", "win32api": "pywin32",
    "pptx": "python-pptx", "edge_tts": "edge-tts", "moviepy": "moviepy",
    "websocket": "websocket-client", "Crypto": "pycryptodome", "qrcode": "qrcode",
    "moviepy.editor": "moviepy", "imageio_ffmpeg": "imageio-ffmpeg",
}
# 仅收录不会误伤普通英文词的外部可执行文件
EXTERNAL_TOOLS = {
    "ffmpeg": "音视频合成", "ffprobe": "音视频探测", "edge-tts": "微软 TTS 配音",
    "tesseract": "OCR 引擎", "pandoc": "文档转换", "soffice": "LibreOffice",
    "libreoffice": "LibreOffice", "wkhtmltopdf": "HTML 转 PDF", "magick": "ImageMagick",
    "xelatex": "LaTeX", "unoconv": "unoconv", "pdftotext": "poppler",
    "msedge.exe": "Edge 浏览器", "chrome.exe": "Chrome 浏览器",
    "gswin64c": "Ghostscript", "gswin32c": "Ghostscript",
}
FONT_HINT = re.compile(r"(?i)(ImageFont\.truetype|[A-Za-z_]*font[A-Za-z_]*\s*[:=]\s*[^\s]+\.(ttf|otf|ttc)|msyh|simhei|sim
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Static analysis

No suspicious patterns detected.