Back to skill

Security audit

Recursive Swarm

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent task-orchestration helper, but its scripts can write outside the intended run folder when given crafted paths, so it belongs in Review before installation.

Review this skill before installing. Use it only with trusted task inputs, keep runs in a dedicated directory, avoid custom --out paths outside the run folder, and treat node IDs as simple dotted numbers like 1.2. Do not use it on sensitive exports unless you are comfortable with summaries, notes, and results being saved in local run artifacts.

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

Warning
Location
scripts/common.py:25
Finding
Unrestricted Path Inputs Permit File Creation and Overwrite Outside the Run Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.py:25-38`, `scripts/upsert_node.py:26-38,59-65,91-93`, `scripts/merge_results.py:11-13,25-41` **Vulnerability Type**: Path traversal and unrestricted filesystem write **Risk Level**: Medium ### Vulnerable Code From `scripts/common.py:25-38`: ```python def node_dir(run_dir: str | Path, node_id: str) -> Path: return run_path(run_dir) / 'nodes' / node_id def spec_path(run_dir: str | Path, node_id: str) -> Path: return node_dir(run_dir, node_id) / 'spec.json' def notes_path(run_dir: str | Path, node_id: str) -> Path: return node_dir(run_dir, node_id) / 'notes.md' def result_path(run_dir: str | Path, node_id: str) -> Path: return node_dir(run_dir, node_id) / 'result.md' ``` From `scripts/upsert_node.py:26-38`: ```python parser.add_argument('--id', required=True, help='Node id, e.g. 1.2') parser.add_argument('--parent-id') parser.add_argument('--goal') parser.add_argument('--type', choices=['research', 'coding', 'ops', 'browser', 'synthesis', 'review']) parser.add_argument('--executor') parser.add_argument('--status', choices=['planned', 'running', 'completed', 'failed', 'waiting_for_approval', 'blocked']) parser.add_argument('--depth', type=int) parser.add_argument('--confidence', choices=['unknown', 'low', 'medium', 'high']) parser.add_argument('--workspace-mode', choices=['artifacts', 'worktree']) parser.add_argument('--approval-required', action='store_true') parser.add_argument('--depends-on', action='append') parser.add_argument('--summary') parser.add_argument('--artifact', action='append') ``` From `scripts/upsert_node.py:59-65`: ```python artifacts = list(dict.fromkeys((existing.get('artifacts', []) + (args.artifact or [])))) if not artifacts: artifacts = [ str(spec_path(run_dir, args.id).relative_to(run_dir)), str(result_path(run_dir, args.id).relative_to(run_dir)), ] ``` From `scripts/upsert_node.py:91-93`: ```python nodes[args.id] = ...[truncated 5028 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Strictly validate node identifiers** Require the documented dotted-numeric format before using an ID: ```python import re NODE_ID_PATTERN = re.compile(r'^[1-9][0-9]*(?:\.[1-9][0-9]*)*$') def validate_node_id(node_id: str) -> str: if not NODE_ID_PATTERN.fullmatch(node_id): raise ValueError('Invalid node ID') return node_id ``` Apply this validation in every script accepting or consuming node IDs, including IDs loaded from `tree.json`. 2. **Enforce a filesystem containment boundary** Resolve both the trusted base and candidate path, then verify containment before any read or write: ```python def confined_path(base: Path, candidate: Path) -> Path: base = base.expanduser().resolve() candidate = candidate.expanduser().resolve() try: candidate.relative_to(base) except ValueError: raise ValueError(f'Path escapes permitted directory: {candidate}') return candidate ``` Use the resolved `run_dir / "nodes"` directory as the boundary for node files. 3. **Restrict merge output destinations** Prefer removing unrestricted `--out` support. If custom names are necessary, accept only a filename or a run-relative path and reject absolute paths and traversal components. Validate the destination before calling `write_text()`. 4. **Validate before performing side effects** Move all path validation ahead of directory creation, file creation, or overwrite operations. Do not rely on the later `relative_to()` call used only for event formatting. 5. **Treat loaded tree data as untrusted** Validate `tree.json` against `references/tree-schema.json` and add semantic checks that the schema does not currently express, including: - node dictionary keys must equal each node's `id`; - all node IDs must follow the permitted format; - child and dependency IDs must refer to existing nodes; - all generated node ...[truncated 610 chars]
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a high-level orchestration system for recursively decomposing and coordinating complex tasks. The actual code chunk does not implement orchestration behavior at all; it only lists stored event records from a run directory. Its primary purpose is inspection/reporting of event logs, which is materially different from the declared purpose. While event listing could be a small supporting utility within a larger orchestration system, this code chunk by itself does not match the declared skill behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is for a higher-level orchestration skill that plans and manages bounded recursive execution of complex tasks. The supplied code chunk does not implement recursive orchestration, decomposition, fan-out/concurrency control, or worktree handling. Instead, it is a narrow state-management helper script for an already-existing task tree, used to mark node status and record notes/results. While such a script could support an orchestration system, its actual behavior is materially different from the declared primary purpose and includes concrete file-writing/state-update capabilities not described in the declaration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill clearly instructs the agent to create run folders, update node state files, append audit logs, and write result artifacts, but it declares no explicit tool scope or permission boundaries. That omission can cause an agent or reviewer to underestimate that the skill performs filesystem mutation, increasing the chance of unintended writes or execution in sensitive locations.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The example explicitly encourages analysis of a user's message export, which is likely to contain highly sensitive personal, professional, and third-party information, but provides no privacy warning, consent check, minimization guidance, or handling constraints. In a reusable example for an orchestration skill, this omission can normalize unsafe processing of personal data and lead users or downstream agents to ingest, store, and summarize sensitive content without appropriate safeguards.

Description-Behavior Mismatch

Medium
Confidence
85% confidence
Finding
The manifest frames recursive-swarm as bounded orchestration for research, audits, synthesis, and coding, and explicitly says not to use it for destructive workflows. This initializer nevertheless treats 'ops' as a first-class mode and root task type, which broadens the skill toward operational workflows outside the described safe scope.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The run defaults unconditionally set allowWorktrees to true, even though the skill contract says git worktrees should be used only for coding leaves inside git repos. If downstream orchestration trusts this flag, non-coding or non-repo tasks could trigger repository/worktree operations in unintended locations, increasing the risk of filesystem modification, data exposure, or unsafe command execution paths.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script writes to result.md via write_text on L43 and L46, which can overwrite prior result content. Although events are appended and the final node is printed, there is no confirmation prompt, explicit user-facing warning, or inline comment/docstring disclosing that result content will be replaced.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This code creates directories and writes notes, results, and spec files, but provides no confirmation prompt, logging, print statement, or explanatory docstring/comment to disclose those filesystem changes. Because this is a code file performing file writes, the lack of any visible warning mechanism matches the missing-user-warning criterion.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The save_tree function overwrites tree.json and append_event appends to events.jsonl, but the code contains no user-facing notice, confirmation, logging, or explanatory comment/docstring about these persistent writes. For code files, silent file modification is in scope when there is no other form of disclosure present in the file.

Static analysis

No suspicious patterns detected.