Back to skill

Security audit

Zaomeng Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated novel-character workflow, but one self-card helper can delete or overwrite files outside its intended folder if given an unsafe card ID.

Install only in a sandboxed or backed-up workspace. Do not pass untrusted values to --card-id or --cards-root, avoid self-card delete until path validation is fixed, and prefer pinned installer versions over @latest.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
tools/_skill_support/persona_review.py:465
Finding
Path Traversal Enables Arbitrary Directory Access, File Overwrite, and Recursive Deletion<![CDATA[ ## Vulnerability Details **File Location**: `tools/_skill_support/persona_review.py:465-526` **Vulnerability Type**: Path traversal and unrestricted filesystem operations **Risk Level**: High ### Vulnerable Code ```python def load_self_card_payload(cards_root: str | Path, card_id: str) -> dict[str, Any]: card_dir = Path(cards_root) / str(card_id or "").strip() if not card_dir.exists(): raise FileNotFoundError(card_id) meta = _load_card_meta(card_dir) profile_path = _resolve_card_profile_path(card_dir) if profile_path is None: raise FileNotFoundError(card_id) profile = load_profile_source(profile_path) fields = read_self_card_fields(profile) return { "card_id": card_dir.name, "fields": fields, "preview": build_self_card_preview(fields), "profile_path": str(profile_path.resolve()), "created_at": str(meta.get("created_at", "")).strip(), "updated_at": str(meta.get("updated_at", "")).strip(), } def save_self_card_payload(cards_root: str | Path, *, card_id: str, fields: dict[str, Any], utc_now: Callable[[], str]) -> dict[str, Any]: normalized = normalize_self_card_fields(fields) validate_self_card_fields(normalized) resolved_card_id = str(card_id or "").strip() or f"card-{uuid4().hex[:10]}" card_dir = Path(cards_root) / resolved_card_id if str(card_id or "").strip() and not card_dir.exists(): raise FileNotFoundError(card_id) card_dir.mkdir(parents=True, exist_ok=True) now = utc_now() meta = _load_card_meta(card_dir) if (card_dir / SELF_CARD_META_FILE).exists() else {} created_at = str(meta.get("created_at", "")).strip() or now profile = build_self_card_profile(normalized) (card_dir / "PROFILE.md").write_text(render_profile_md(profile), encoding="utf-8") (card_dir / SELF_CARD_META_FILE).write_text( json.dumps( { "card_id": resolved_card_id, "creat ...[truncated 4221 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Enforce a strict card identifier format** Accept only simple identifiers that cannot express paths: ```python import re CARD_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,64}$") def validate_card_id(card_id: str) -> str: value = str(card_id or "").strip() if not CARD_ID_PATTERN.fullmatch(value): raise ValueError("Invalid card identifier") return value ``` 2. **Centralize secure path resolution** Resolve the root and candidate and verify containment before every load, save, or delete operation: ```python def resolve_card_dir(cards_root: str | Path, card_id: str) -> Path: safe_id = validate_card_id(card_id) root = Path(cards_root).resolve() candidate = (root / safe_id).resolve() if not candidate.is_relative_to(root): raise ValueError("Card path escapes the configured card root") return candidate ``` For Python versions without `Path.is_relative_to`, use `candidate.relative_to(root)` and reject `ValueError`. 3. **Explicitly reject absolute paths and traversal components** Defense in depth should reject IDs where `Path(card_id).is_absolute()` is true or where any component equals `..`, even if strict identifier validation is already present. 4. **Apply validation consistently** Replace direct path joins in `load_self_card_payload`, `save_self_card_payload`, and `delete_self_card_payload` with the same secure resolver. Validation must not be limited to deletion. 5. **Restrict deletion to expected card artifacts** Avoid recursively deleting every item beneath a selected path. Delete only files explicitly owned by the self-card feature, such as `PROFILE.md` and the known metadata file, and refuse deletion when unexpected content is present. 6. **Harden against links** Reject a card directory if it is a symbolic link. When stronger local-adversary protection is needed, inspect path components and ...[truncated 507 chars]
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (56)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
代码内容明显是压缩后的通用前端依赖库,而不是面向中文小说处理的业务逻辑。片段中可见大量 HTML/SVG/MathML 标签与属性白名单、Trusted Types/DOMPurify 风格的清洗逻辑,以及 KaTeX 数学渲染相关函数、符号路径和排版配置。这些行为与声明的“中文小说人物蒸馏、关系抽取、关系图谱导出与角色对话准备”没有实质对应关系。即使 Mermaid 可能可用于图谱可视化,当前片段也没有实现小说关系抽取或图谱导出本身,因此属于描述与实际行为的明显不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
该代码片段明显是压缩/混淆后的第三方前端库资源(路径也显示为 assets/vendor/mermaid-11.14.0.min.js)。内容集中在数学符号表、字体 metrics、Span/Anchor/SvgNode/PathNode/MathML 节点构造、公式排版与渲染辅助逻辑。这与声明中的“中文小说人物蒸馏、关系抽取、关系图谱导出与角色对话准备”没有实质关联。代码未体现任何文本解析、中文 NLP、实体识别、关系抽取、图谱导出或角色建模能力,反而实现了未声明的渲染能力,因此属于明显的描述-行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个面向中文小说内容处理的 NLP/知识图谱技能,应当看到人物识别、关系抽取、结构化档案生成、图谱导出或对话上下文整理等逻辑。但代码内容明显是压缩后的前端库代码,包含大量 LaTeX/KaTeX 命令处理、解析器、词法器、宏展开器、数组/分式/根式/上下标/链接/HTML 扩展等数学排版相关实现。这与声明用途在主功能、能力范围和代码领域上都完全不一致。未见任何小说文本分析、中文处理、实体关系抽取或图谱导出逻辑,因此应判定为明显不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个面向中文小说内容分析的技能,应当看到人物抽取、关系识别、档案结构化、关系图谱导出或对话上下文构建等逻辑。但该代码完全是通用前端可视化库实现:包含 Mermaid 图表检测/注册、C4 diagram parser、KaTeX 渲染、DOM 清洗、SVG 处理等,与小说人物蒸馏或关系抽取无直接对应关系。虽然“关系图谱导出”在宽泛意义上可能涉及图形库,但这里的代码并非专门导出小说关系图谱,而是底层通用渲染组件,且主要聚焦 C4/diagram 解析与显示。因此其主要目的与声明严重不符,属于明显的描述-行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
代码内容明显来自 vendor 目录下的压缩前端依赖库(mermaid 及其相关解析/渲染组件),其实际职责是解析指令、处理 YAML/Markdown、生成图表与 HTML/SVG 渲染结果。未见任何与中文小说人物抽取、人物蒸馏、关系抽取、关系图谱语义构建或角色对话上下文准备直接相关的领域逻辑,如文本语义分析、实体识别、关系分类、小说角色档案构建等。因此,声明的用途与实际代码行为存在显著不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个面向中文小说人物分析与关系抽取的技能,应包含文本理解、人物信息提取、关系建模或对话上下文整理相关逻辑。但实际代码完全是 Mermaid/绘图库的最小化前端实现片段,包含文本标签创建、SVG path 生成、圆形/矩形/多边形/云形等节点绘制、rough/hand-drawn 风格渲染、图标替换与图像布局等功能。未见任何小说文本处理、中文 NLP、人物蒸馏、关系抽取、图谱导出或角色对话准备逻辑。其主要目的与声明严重不符,因此属于明显描述-行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个面向中文小说内容处理的技能,应执行人物蒸馏、关系抽取、图谱导出或多角色对话上下文准备。实际代码则明显属于 Mermaid 11.14.0 的前端渲染实现:包含 shape map、FlowDB、addVertex/addLink、insertNode/insertEdge、marker 定义、tooltip/click 事件、SVG path 与 cluster 渲染等功能。这些都是通用图表绘制与交互能力,而不是小说 NLP 或关系抽取逻辑。虽然“关系图谱导出”在广义上可能最终需要可视化,但该代码仅提供底层图形渲染,不体现任何小说领域的数据抽取、结构化建模或对话准备能力,因此与声明的主要用途存在实质性不符。

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
声明描述的是一个较高层的小说人物与关系信息抽取/导出技能,但该代码块实际只提供前置预处理能力。它读取小说文件、处理编码、解析 epub、加载角色别名、匹配角色出现句子,并构造供后续任务使用的 excerpt payload(含 matched/missing characters、excerpt_strategy、excerpt_stages)。这与声明中的“角色对话准备”部分有一定关联,因为代码确实会挑选含对话/心理描写的句子以增强上下文;但核心声明里的‘人物蒸馏、关系抽取、关系图谱导出’在该代码中均没有实现。因此描述显著高于实际代码能力,属于实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
声明描述了一个面向中文小说内容分析的技能,重点包括“人物蒸馏、关系抽取、关系图谱导出、角色对话准备”。但该代码块并未执行小说文本分析、实体关系识别、图谱构建或图数据导出。它处理的是已给定 profile 数据的标准化与文件落盘:解析 markdown/json、合并 persona 文件、生成多个角色设定分片文件、写入 NAVIGATION 和 ARTIFACT_STATUS 文件,并维护 MEMORY/AGENTS 等运行时辅助内容。与“角色对话准备”部分有一定相关性,但只覆盖其一部分;而声明中的“关系抽取、关系图谱导出、人物蒸馏”均未在代码中体现。因此描述不能准确代表该代码块的实际行为,属于明显不完全且部分失配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
声明描述的是一个较广的小说人物蒸馏/关系抽取/关系图谱导出/对话准备技能,但该代码块的实际功能更窄且部分不同:它集中在人物校对表单字段管理、PROFILE.md 读写、补全提示词生成与响应解析,以及“self card”原创角色卡 CRUD。虽然“基于小说内容生成结构化人物档案”与本代码部分吻合,但声明中的关键能力——关系抽取、关系图谱导出、多角色对话上下文准备——在该代码中没有体现。反过来,代码还包含未在声明中明确提到的原创角色卡生成与删除等本地内容管理能力。因此描述不能准确代表该代码块的实际行为,属于实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
该代码没有体现人物蒸馏、人物关系抽取、关系图谱导出等声明中的核心能力,也没有处理小说人物档案抽取或关系结构化输出。相反,它围绕 scene_cards 和 dialogue scene recommendation 展开,核心目的是为多角色对话选择/推荐场景并生成开场与转场文案。虽然这与“角色对话准备”存在一定边缘关联,但其主要行为是场景推荐与对话推进控制,属于声明中未明确覆盖的能力,且与所宣称的主要用途明显不一致,因此应判定为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
声明强调的是‘中文小说人物蒸馏、关系抽取、关系图谱导出与角色对话准备’,其中核心能力应包括人物档案提炼、关系抽取和图谱导出。实际代码没有体现任何人物抽取、关系识别、图谱构建或小说内容分析逻辑;相反,它专门处理 dialogue suggestion 的请求/响应打包,包括生成 messages、compact_messages、retry_messages,以及解析 response_file 中的模型输出。虽然‘角色对话准备’与对话建议有一定弱相关性,但代码的直接用途是对话建议协议封装,不足以覆盖声明中的主要功能集合,因此属于描述与行为存在实质性偏差。

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
声明描述的是一个面向中文小说的人物蒸馏、关系抽取、图谱导出和对话准备的技能;而代码实际只实现了 persona review/autofill 辅助工具:校验字段是否可自动补全、加载 persona review payload、生成供宿主执行 LLM 的 messages 与 retry_messages,或解析 response_file 中的模型输出。代码没有展示任何关系抽取、关系图谱导出、或多角色对话上下文组装逻辑,其核心目的与声明存在明显偏差。虽然都与“人物档案”方向略有关联,但该代码的主要功能更窄且不同,因此应判定为描述与行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述聚焦于中文小说的人物蒸馏、关系抽取、关系图谱导出和角色对话准备;而实际代码并未体现人物信息提取、关系抽取或图谱导出逻辑。相反,它是一个独立的命令行封装器,专门从上下文 JSON 构建“scene recommendation bundle(场景推荐包)”,且其 argparse 描述明确提到 transition 和 auto-continue hints。即使“角色对话准备”与场景推荐存在轻微关联,这段代码的主要目的仍然是场景推荐与对话流程引导,而不是声明中强调的人物/关系结构化处理,因此属于实质性用途不符。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述聚焦于“中文小说人物蒸馏、关系抽取、关系图谱导出与角色对话准备”,意味着代码应围绕小说文本分析、人物关系识别和图谱/对话上下文生成展开。但实际代码是一个 self card 管理 CLI:依赖 persona_review 模块提供字段定义与 CRUD/解析函数,支持 blank/list/get/save/delete/build-random-payload/parse-random-response 等模式,主要作用是管理和生成自我/宿主插入式角色卡。代码中没有任何针对小说内容的解析、人物关系抽取、关系图谱导出逻辑,也没有明显的多角色对话上下文整备流程。因此其主要用途与声明明显不符,属于实质性能力与目的不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是面向小说内容处理的能力:人物蒸馏、关系抽取、图谱导出和角色对话上下文准备。但实际代码并不处理小说文本、人物信息、关系抽取或图谱导出;它只是解析命令行参数,并调用 update_run_manifest 将标准化进度信息写入 run_manifest.json。虽然参数里出现了 character、graph-status、chunk-capability 等字段,说明它可能服务于相关工作流,但该代码本身的直接功能是工作流进度记录,而非声明中的核心业务能力。因此这属于明显的描述与行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
声明描述的是内容处理型能力:中文小说人物蒸馏、关系抽取、关系图谱导出和角色对话准备。而实际代码并不执行人物抽取、关系抽取、图谱生成或对话上下文构建;它只是验证这些上游流程是否已经产出预期文件,并记录验证状态。代码的核心行为包括:解析命令行参数、根据给定人物根目录和可选关系文件构建完成度状态、输出验证结果JSON、写入状态文件,以及在提供run_manifest时更新工作流元数据。因此其主要用途是“工作流验证/状态管理”,与声明的“小说角色与关系内容生成”存在实质性不一致。

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
ew Error("Expected non-null, but got "+String(e));return e},"assert"),gDe=o(function(e){var r=/^[\x00-\x20]*([^\\/#?]*?)(:|&#0*58|&#x0*3a|&colon)/i.exec(e);return r?r[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(r[1])?null:r[1].toLowerCase():"_relative"},"protocolFromUrl"),ui={deflt:lDe,escape:dDe,hyphenate:uDe,getBaseElem:PX,isCharacterBox:pDe,protocolFromUrl:gDe},Wx={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format <type>"},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render e
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

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
e) else 'no'}",
                f"- role: {meta.get('role', '')}",
                f"- behaviors: {meta.get('behaviors', '')}",
                f"- write_policy: {meta.get('write_policy', 'manual_edit')}",
                "",
            ]
        )
    return "\n".join(lines).rstrip() + "\n"


def render_navigation_override_md() -> str:
    return (
        "# NAVIGATION\n"
        "<!-- Optional overrides for the generated navigation map.\n"
        "Use the same key format as NAVIGATION.generated.md.\n"
        "-->\n"
    )


def persona_file_exists(persona_dir: Path, base_name: str) -> bool:
    return (persona_dir / f"{base_name}.md").exists() or (persona_dir / f"{base_name}.generated.md").exists()


def persona_file_is_active(
    persona_dir: Path,
    base_name: str,
    persona_file_catalog: Dict[str, Dict[str, Any]],
) -> bool:
    if not persona_file_catalog.get(base_name, {}).get("optional", True):
        return True
    return persona_file_e
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This markdown file is entirely presented in Chinese, including the title and all user-facing installation instructions. The policy allows locale constraints only when users are given a language option or the restriction is clearly documented and justified, neither of which appears here despite a referenced `README_EN.md` existing.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This manifest uses Chinese headings and descriptions throughout, which can impose a specific language on users or maintainers without any opt-in or explanation. The policy for natural-language violations applies to all file types and specifically covers forced language or locale constraints.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill explicitly describes generating and writing profiles, memories, manifests, and graph artifacts into the local runtime, but it does not clearly warn users that local files may be created or overwritten. In an agent-hosted environment, silent persistence can modify workspace state unexpectedly, causing data loss, contamination of future runs, or leakage of sensitive text into durable artifacts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The README instructs users to execute `npx clawhub@latest install zaomeng-skill`, which fetches and runs remote package code at install time without a pinned immutable version. This creates a supply-chain risk: if the package or a dependency is compromised later, users may execute attacker-controlled code simply by following the documented install step.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The README recommends running `npx clawhub@latest install zaomeng-skill`, which fetches and executes the latest published package at install time without pinning a specific trusted version. If the upstream package is compromised, typo-squatted, or a malicious release is published, users could execute attacker-controlled code during installation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises and orchestrates multiple Python helper scripts that read and write local files, update manifests, and may participate in graph export workflows, but it does not declare an explicit permission or allowed-tools scope. This creates an overly broad trust boundary: a host may grant more capabilities than necessary, making unintended file, shell, or network access harder to constrain and audit.

Static analysis

No suspicious patterns detected.