Back to skill

Security audit

multi-agent-pro

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its multi-agent orchestration purpose, but its local web dashboard exposes pipeline data too broadly and has concrete unsafe file-read and HTML injection issues.

Install only if you are comfortable with a local orchestration tool that stores task outputs on disk and exposes them through a localhost dashboard. Avoid placing secrets or private account data in pipeline outputs, do not run the dashboard against directories containing unrelated JSON files, and prefer fixing the dashboard path containment and HTML escaping issues before using it with sensitive workflows.

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

Warning
Location
scripts/web_dashboard.py:356
Finding
Unauthenticated Arbitrary JSON File Read Through Dashboard Path Traversal## Vulnerability Details **File Location**: `scripts/web_dashboard.py`, lines 356–363 **Vulnerability Type**: Path traversal leading to unauthorized local file disclosure **Risk Level**: Medium ### Vulnerable Code ```python if parsed.path == '/detail': params = parse_qs(parsed.query) fname = params.get('file', [''])[0] if not fname: self._send_json({'error': '缺少 file 参数'}, 400) return state_path = os.path.join(self.state_dir, fname) state = get_state_detail(state_path) ``` The resulting path is passed to the following file-reading function at lines 87–94: ```python def get_state_detail(state_path): """读取单个流水线的完整状态(供详情页/甘特图使用)""" try: with open(state_path, 'r', encoding='utf-8') as f: state = json.load(f) return state except (json.JSONDecodeError, IOError): return None ``` ### Technical Analysis The `/detail` route accepts the `file` query parameter from an unauthenticated HTTP request. The value is appended to the configured state directory with `os.path.join()`, but the resolved path is never checked to ensure it remains inside that directory. Path normalization alone would not be sufficient unless the normalized path is also constrained to the intended directory. Values containing `../` components can escape `state_dir`. An absolute path can also cause `os.path.join()` to discard the state directory entirely. The selected file is opened with the dashboard process's filesystem permissions. The only content restriction is that it must parse as JSON. The dashboard is bound to `127.0.0.1`, which limits remote exposure but does not authenticate local callers; other local users, browser-driven requests, or local processes may still reach the service. ### Attack Path 1. The victim starts the dashboard against a directory containing pipeline state files. 2. An attacker capable of sending requests to the local dashboard identifies a readable JSON file outside that directory. 3 ...[truncated 1013 chars]
Remediation
## Remediation Suggestions 1. Resolve the configured directory and requested path before opening the file: ```python from pathlib import Path base = Path(self.state_dir).resolve() requested = (base / fname).resolve() try: requested.relative_to(base) except ValueError: self._send_json({"error": "Invalid file path"}, 400) return ``` 2. Reject absolute paths, empty names, path separators, and traversal components. 3. If only direct child files are required, accept a basename and enforce: ```python if fname != os.path.basename(fname): self._send_json({"error": "Invalid filename"}, 400) return ``` 4. Build a server-side map of pipeline identifiers to known state files and let clients submit only an opaque identifier, not a filesystem path. 5. Require the target to be a regular `.json` file discovered by `find_pipelines()`. 6. Consider authenticating all dashboard routes, not only the approval endpoint, when pipeline state can contain sensitive information.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/web_dashboard.py:164
Finding
Stored HTML and JavaScript Injection in the Local Web Dashboard## Vulnerability Details **File Location**: `scripts/web_dashboard.py`, lines 164–215 and 304–310 **Vulnerability Type**: Stored cross-site scripting through unsafe HTML and inline JavaScript interpolation **Risk Level**: Medium ### Vulnerable Code The detail page inserts pipeline metadata directly into HTML and serialized node output directly into an executable script context: ```python return f'''<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>流水线详情 - {pipeline_name}</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> ... <body> <div class="container"> <a class="back" href="/">返回列表</a> <h1>{pipeline_name}</h1> <p class="meta">ID: {pipeline_id} | 状态: {state.get("status", "")} | 节点: {len(nodes)}</p> {approval_html} ... <script> const chart = echarts.init(document.getElementById('gantt')); const categories = {categories_json}; const rawData = {chart_data}; const option = {{ tooltip: {{ trigger: 'item', formatter: function(params) {{ const d = params.data; const node = {json.dumps({nid: n.get('output_data', {}) for nid, n in nodes.items()}, ensure_ascii=False)}[d.name]; return '<b>' + d.name + '</b><br/>' + '状态: ' + d.value[3] + '<br/>' + '<pre style="max-height:200px;overflow:auto;font-size:11px;">' + JSON.stringify(node, null, 2) + '</pre>'; }} }}, ``` The index page also inserts state-derived names and filenames into markup without escaping: ```python rows += f''' <tr> <td><a href="/detail?file={p['file']}">{p['name']}</a></td> <td><code>{p['id'][:20]}</code></td> <td style="color:{status_color};font-weight:500;">{p['status']}</td> <td><span title="已完成">{p['completed']}</span> / <span title="总">{p['node_count']}</span></td> <td>{p['updated_at']}</td> </tr>''' ``` ### Technical Analysis State-file values are treated as trusted presentation data even though pipeline names, node identifiers, and node output may originate from imported wor ...[truncated 2729 chars]
Remediation
## Remediation Suggestions 1. Apply context-specific HTML escaping to every state-derived value inserted into markup: ```python from html import escape safe_name = escape(str(pipeline_name), quote=True) safe_id = escape(str(pipeline_id), quote=True) ``` 2. URL-encode filenames and other query-string values with `urllib.parse.urlencode()` rather than inserting them directly into `href` attributes. 3. Do not insert ordinary `json.dumps()` output directly into an executable `<script>` block. 4. Prefer serving visualization data from a JSON endpoint and loading it with `fetch()`. 5. If JSON must be embedded in HTML, place it in a non-executable `application/json` element and escape characters significant to the HTML parser, especially `<`, `>`, `&`, U+2028, and U+2029. 6. Build tooltip content with DOM nodes and `textContent` instead of concatenating HTML strings. 7. Add a restrictive Content Security Policy. Remove inline JavaScript where possible and avoid allowing `unsafe-inline`. 8. Validate state files against an explicit schema and constrain identifiers and display names, while retaining output encoding as the primary defense. 9. Do not keep approval credentials in browser local storage. Use a more narrowly scoped authentication mechanism and require server-side authorization for every state-changing request.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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 (81)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
声明描述的是一个功能完整的多Agent流水线编排平台/基础设施,而给出的代码片段只是其中一个“DAG 验证器”子组件。它主要做输入文件安全读取、Schema 校验、控制流节点字段检查、依赖合法性检查、环检测、孤立节点检测和拓扑排序输出。虽然代码中的节点类型与审批/控制流概念与声明中的部分术语相关,但仅停留在静态验证层面,没有实现所宣称的大部分核心能力,尤其是编排执行、可视化、状态共享、恢复机制、Web/MCP 暴露与成本桥接等。因此描述与该代码片段的实际行为存在明显且实质性的不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
该代码块的核心用途是“错误恢复引擎”,属于所宣称大系统中的一个子模块,具体提供 retry/fallback/impact 三类命令,围绕节点失败后的恢复与影响分析进行状态管理。虽然声明中提到‘统一错误恢复命令’、‘任务级重试策略’和‘错误重断点续传’,这些与代码部分吻合,但声明把技能整体描述为一个覆盖编排、调度、可视化、审批、控制台、MCP暴露、模板库和成本桥接的完整平台,而当前提供的代码块并未实现或体现这些大部分能力。因此从“描述是否准确代表该代码块实际行为”角度看,存在明显夸大和范围不符,属于描述与行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
该代码块确实属于“AI即编排器,脚本提供基础设施”的一部分,并与声明中的一部分能力吻合:基于DAG/状态的控制流编排、分支、循环、子流水线、一定程度的状态共享与重试增强、子流水线隔离执行等。但声明描述的是一个范围很广的完整编排平台,而本代码只实现其中的控制流子系统。按照审计标准,若声明让人预期该技能具备Web控制台、可视化、人工审批、MCP接口、成本桥接等能力,而代码中完全未体现,则属于描述显著超出实际代码行为的情况。虽然单个代码块不必实现全部系统功能,但就“所 supplied code chunk 实际做什么”而言,其功能边界明显小于声明,因此应判定为存在不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个功能非常广泛的多Agent流水线编排系统,而提供的代码片段只是其中一个很小的辅助组件:硬件检测与参数推荐。该脚本不包含任何编排引擎、任务调度、审批、可视化、Web 控制台、MCP 接口或成本桥接逻辑。虽然声明中提到“硬件自适应参数”,这与脚本行为部分相关,但这只覆盖了声明中的一个子点,无法代表整体技能的主要用途。就当前代码片段而言,其实际主功能与声明的主功能存在显著偏差,因此应判定为描述与行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个功能完整的多Agent流水线编排系统,而提供的代码片段只是一个配套的子流水线注册表脚本,主要负责本地 JSON 注册、查询与校验。虽然它与“编排基础设施”存在弱相关性,但其主功能明显更窄,且绝大多数核心声明能力在代码中完全不存在。因此描述与实际行为存在明显不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
该代码块的主功能与声明的整体技能能力明显不一致。代码只负责“执行结果可视化”:加载 JSON 状态文件,计算统计信息,生成 Markdown 报告和静态 HTML 甘特图,并在报告中展示节点输出、错误、重试次数、成本明细与简单后续建议。虽然它覆盖了声明中的一小部分‘执行报告生成’和‘HTML甘特图可视化’,但声明将技能描述为完整的编排引擎/控制台/MCP 接口/审批系统/恢复系统等,而这些核心能力在此代码中均不存在。因此这是明显的描述-行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
声明描述的是一个广泛的“多Agent流水线编排平台/基础设施”,而代码块只覆盖其中很窄的一部分:快照保存、恢复、差异比较和保留策略。虽然其中与“错误重断点续传”“历史执行对比”存在部分相关性,但实现层面仅限节点快照级别,本身并不构成所宣称的完整编排、可视化、审批、Web 控制台、MCP 接口或成本桥接等能力。代码也没有出现额外高风险的未声明资源访问;问题主要在于声明显著夸大了实际代码功能,属于描述与实际行为的明显不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
该代码片段的实际功能非常聚焦,是一个本地CLI状态恢复工具,服务于编排系统中的“错误恢复/断点续传/快照恢复”这一子能力。虽然它与声明中的“错误重断点续传、统一错误恢复命令”部分一致,也确实涉及DAG依赖的下游节点重置,但它并不实现声明中绝大多数核心能力,如调度编排、报告、可视化、审批、Web控制台、MCP接口、成本桥接等。因此若将该技能描述视为对此代码块功能的准确表述,则存在明显不匹配:描述覆盖的是完整编排平台,而代码只是其中一个恢复模块。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
该代码片段的主用途是“模板库管理器”,负责发现和展示预置模板、探测模板声明的外部依赖是否存在、并渲染模板中的pipeline JSON。虽然声明文本中提到“官方流水线模板库”,这与当前代码部分一致,但声明的主体是一个完整的多Agent编排引擎及大量运行时/可视化/交互/MCP能力,而本片段没有实现这些核心能力。代码也没有执行流水线、没有DAG求解、没有状态持久化、没有人工审批机制、没有Web界面、没有MCP接口、没有成本桥接。唯一额外的资源访问是读取本地模板文件、检查用户目录/工作区目录中的skill安装标记、以及尝试import模块,这些都可视为模板依赖探测的实现细节,不构成额外恶意能力。但从“描述是否准确代表代码实际行为”角度看,描述明显大幅超出该代码片段实际功能范围,因此应判定为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个功能复杂的多Agent编排系统;而提供的代码片段只是其中非常边缘的一项‘版本更新提醒’子功能。该脚本的主行为是访问 raw.githubusercontent.com 读取远程 SKILL.md,解析 version 字段并与本地版本比较后打印提示。它不展示编排、调度、审批、可视化、Web 控制台、MCP 工具暴露等核心能力。虽然声明中确实提到了‘版本更新提醒’,但若用该代码片段代表整个技能,其实际行为与声明的主要用途明显不一致,因此应判定为描述与代码行为不匹配。

Ae1

High
Category
analysis-evasion
Content
**条件表达式语法**(`scripts/condition_evaluator.py`):
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**条件表达式语法**(`scripts/condition_evaluator.py`):
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The module claims path normalization and traversal protection, but restore_to_node accepts an arbitrary state_path and then reads and writes files relative to it, including derived checkpoint and snapshot locations, without any visible validation or confinement to an approved workspace. In an orchestration skill that shares state across agents and exposes recovery operations, this can let a caller operate on unintended filesystem locations, causing unauthorized file read/write or corruption of other pipeline states.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises and documents capabilities that imply file read/write, shell execution, environment access, and network access, but it does not declare any explicit tool scope or permission boundaries. In an agent ecosystem, missing scope declarations can cause the agent to invoke this skill with broader privileges than users expect, increasing the chance of unauthorized file access, command execution, or outbound requests.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
SQP-3 适用于所有文件类型中的自然语言策略问题。该技能的描述、示例触发词、说明与命令解读均默认且强制为中文,没有说明可根据用户偏好切换语言,也没有给出这是地区/合规限定场景的理由。

Vague Triggers

Medium
Confidence
93% confidence
Finding
作为 markdown 文件,需要检查触发描述是否足够具体。这里的示例短语如“查看流水线执行状态”“生成执行报告”“生成流水线甘特图”等缺少明确上下文约束,且全文后续多个模块也复用类似宽泛短语,未给出排除条件或限定必须针对本编排引擎/特定命令上下文触发。

Vague Triggers

Medium
Confidence
96% confidence
Finding
SQP-1 适用于 markdown 文件中的触发词。文档在多个模块中把高度通用的自然语言词语直接定义为触发词,例如 L0244 的“可视化”、L0270 的“暂停”、L0511 的“生成报告”、L0628 的“成本”、L0810 的“周报生成”等,这些都与常见日常请求高度重叠,且未提供负例或限定作用域。

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The entire guide is written in Chinese, including the title and instructional content, with no indication that language selection is optional or that the skill is region-specific. Under the language/locale policy rule, forcing a specific language without user opt-in is a natural-language policy concern.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The FAQ states that the state file is stored locally and 'not uploaded to any server,' but the skill metadata explicitly advertises a Web console and MCP stdio JSON-RPC exposure, which can enable remote viewing, transfer, or processing of pipeline state in practice. This creates a misleading security guarantee that may cause users to place sensitive data in state or outputs under false assumptions about isolation and non-network exposure.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The entire skill reference is written in Chinese and provides operational instructions exclusively in that language, with no indication that users may choose another language or that the locale constraint is required for a region-specific purpose. This can violate language/locale policy when a skill forces a specific language without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This code file contains natural-language descriptions, help text, comments, and error messages entirely in Chinese, including the module description and user-facing CLI output. Under the policy for natural-language violations, forcing a specific language without user opt-in is a locale/language policy issue.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The module-level security documentation states that function calls are categorically forbidden, but the evaluator actually permits a broad whitelist of callable functions, including regex helpers. This mismatch can cause downstream developers or reviewers to overtrust the sandbox and enable unsafe expressions in higher-risk workflows, especially because some allowed functions can trigger expensive processing on attacker-controlled input.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
# 允许的白名单正则函数(安全子集,禁止 re.compile/re.sub 等危险操作)
_WHITELISTED_RE_FUNCTIONS = {
    'match': lambda pattern, string: bool(__import__('re').match(pattern, string)),
    'search': lambda pattern, string: bool(__import__('re').search(pattern, string)),
    'findall': lambda pattern, string: __import__('re').findall(pattern, string),
}
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
# 允许的白名单正则函数(安全子集,禁止 re.compile/re.sub 等危险操作)
_WHITELISTED_RE_FUNCTIONS = {
    'match': lambda pattern, string: bool(__import__('re').match(pattern, string)),
    'search': lambda pattern, string: bool(__import__('re').search(pattern, string)),
    'findall': lambda pattern, string: __import__('re').findall(pattern, string),
}
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
_WHITELISTED_RE_FUNCTIONS = {
    'match': lambda pattern, string: bool(__import__('re').match(pattern, string)),
    'search': lambda pattern, string: bool(__import__('re').search(pattern, string)),
    'findall': lambda pattern, string: __import__('re').findall(pattern, string),
}

# 允许的二元算术运算符(用于 score * 2 > 100 这类简单运算)
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Static analysis

No suspicious patterns detected.