Back to skill

Security audit

toolchain-orchestrator

Security checks for vulnerabilities and agentic risk

Overview

This skill is a command orchestrator with useful disclosed functionality, but it needs Review because it executes shell commands from chain files, unsafely inserts prior command output into later commands, and adds persistent learning/self-modification behavior with weak scoping.

Install only if you intend to review and run trusted chain files as code. Use a sandbox or least-privileged account, avoid passing untrusted command output into {{node_id}} placeholders, inspect generated output paths, and disable or remove the learner if you do not want persistent operation history, preferences, notes, or SKILL.md updates.

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/orchestrator.py:167
Finding

Shell Command Injection Through Unescaped Upstream Output Substitution

Content
View full analysis

Vulnerability Details

File Location: scripts/orchestrator.py, lines 167–170
Vulnerability Type: Command injection
Risk Level: High

Vulnerable Code

python
cmd = n.get("run", "")
for dep, val in outputs.items():
    cmd = cmd.replace(f"{{{{{dep}}}}}", (val or "").strip())
print(f"▶ 运行 {k}: {cmd}")
r = subprocess.run(cmd, shell=True, capture_output=True, text=True)

Technical Analysis

Captured output from previously executed nodes is inserted verbatim into a downstream command string. The resulting string is then passed to subprocess.run with shell=True.

Consequently, shell metacharacters and command substitutions contained in an upstream node's output are interpreted as shell syntax rather than treated as data. Examples of dangerous output include semicolon-separated commands, command substitutions, pipelines, redirects, and logical operators.

The documentation states that chain definitions should be trusted, but this does not eliminate the vulnerability. A trusted chain may process untrusted files, repository contents, API responses, user input, or other external data and print that data to stdout. If the stdout is subsequently referenced through a {{node_id}} placeholder, an attacker who controls that data can cross the data-to-code boundary.

Attack Path

  1. A chain contains an upstream node whose stdout is captured.
  2. The upstream node processes attacker-controlled data and prints a value containing shell syntax, such as $(malicious-command) or ; malicious-command.
  3. A downstream node includes the upstream placeholder, such as tool --value "{{upstream}}".
  4. The orchestrator replaces the placeholder with the unescaped stdout.
  5. The orchestrator executes the assembled command with shell=True.
  6. The shell interprets the injected syntax and executes the attacker's command with the orchestrator process's privileges.

Impact Assessment

Successful exploita ...[truncated 577 chars]

Remediation
View remediation

Remediation Suggestions

  1. Avoid constructing shell command strings from dynamic data.
  2. Define node commands as argument arrays and invoke them with subprocess.run(arguments, shell=False).
  3. Transfer upstream output through stdin, temporary data files with controlled permissions, or explicitly defined environment variables instead of interpolating it into executable syntax.
  4. If shell execution is an unavoidable compatibility requirement, apply platform-appropriate quoting such as shlex.quote to every substituted value. Treat this as defense in depth rather than the preferred design.
  5. Add a chain schema that distinguishes static executable arguments from dynamic data arguments.
  6. Run workflows under a dedicated, least-privileged account or sandbox without unnecessary credentials, filesystem access, or network access.
  7. Add regression tests using outputs containing semicolons, command substitutions, redirects, pipes, quotes, newlines, and logical operators to verify that these values cannot trigger command execution.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/orchestrator.py:173
Finding

Arbitrary File Write Through Path Traversal in Node Identifiers

Content
View full analysis

Vulnerability Details

File Location: scripts/orchestrator.py, lines 173–175
Vulnerability Type: Path traversal leading to arbitrary file overwrite
Risk Level: Medium

Vulnerable Code

python
if n.get("capture") == "stdout":
    outputs[k] = stdout
    open(os.path.join(outdir, f"{k}.out"), "w", encoding="utf-8").write(stdout)

Technical Analysis

The node identifier k is sourced directly from the keys in the supplied chain definition. It is concatenated with the .out suffix and joined to the output directory without validation or a containment check.

os.path.join does not prevent traversal. A node identifier containing parent-directory components such as ../../target can resolve outside outdir. Depending on the identifier and platform path semantics, an absolute path component may also cause the configured output directory to be discarded.

The file is opened in write mode, so an existing writable file at the resolved path is truncated and replaced with the node's stdout. Exploitation requires the attacker to control or influence the chain definition, which the documentation expects to be trusted; however, the code does not enforce that trust boundary.

Attack Path

  1. An attacker supplies or modifies a chain definition.
  2. The attacker defines a node identifier containing traversal components, for example ../../target.
  3. The node is configured with "capture": "stdout" and a command that prints attacker-selected content.
  4. The orchestrator executes the node successfully.
  5. It constructs a path equivalent to outdir/../../target.out.
  6. The operating system normalizes the traversal components, and the stdout is written outside the intended output directory.
  7. If a sensitive target with the resulting .out suffix is writable, its contents are replaced.

Impact Assessment

An attacker can create or overwrite files ending in .out at locations writable by the ...[truncated 458 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict node identifiers to a conservative allowlist, such as ^[A-Za-z0-9_-]+$.
  2. Reject identifiers containing path separators, parent-directory references, null bytes, or absolute-path syntax.
  3. Resolve both the output directory and candidate output path with pathlib.Path.resolve().
  4. Before writing, verify that the candidate path is a descendant of the resolved output directory, for example with candidate.relative_to(output_root).
  5. Use a generated safe filename or stable identifier-to-filename mapping rather than using the raw node identifier.
  6. Open output files using safe creation semantics where overwrite is not required, and apply restrictive file permissions.
  7. Add tests covering ../, nested traversal, absolute paths, mixed path separators, and platform-specific path forms.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The documented behavior materially diverges from the stated purpose: the file claims safe, auditable DAG orchestration but also introduces persistent learning, history, and preference storage unrelated to the manifest description. Description-behavior mismatch is dangerous because reviewers and users may authorize the skill for orchestration while it silently expands into local data persistence and broader resource access, undermining informed consent and trust boundaries.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The file implements a cross-skill learning/telemetry component, not the declared toolchain DAG orchestrator capability. This scope mismatch is dangerous because it introduces hidden data collection and persistence behavior that users and callers would not reasonably expect from this skill, increasing the chance of unauthorized monitoring and trust-boundary violations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

resolve_skill_dir allows the script to target arbitrary directories or alternate skill names under ~/.workbuddy/skills, enabling read/write access to other skills' learned_patterns.json files. In the context of a toolchain orchestrator, this is unjustified cross-skill state access that can leak operational history, modify preferences, or poison another skill's behavior.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

This is a classic tool-parameter abuse issue: untrusted data from the chain definition and from upstream node outputs is treated as executable shell syntax. Because downstream commands are built by plain string replacement, an attacker can smuggle shell separators, substitutions, or redirections through {{node_id}} values and pivot one tool's output into arbitrary command execution in later steps.

Content

Scanner excerpt · scripts/orchestrator.py (reported line 172)May include surrounding context.

python
for dep, val in outputs.items():
            cmd = cmd.replace(f"{{{{{dep}}}}}", (val or "").strip())
        print(f"▶ 运行 {k}: {cmd}")
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        stdout = r.stdout
        if n.get("capture") == "stdout":
            outputs[k] = stdout

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill advertises and demonstrates shell execution and file-writing behavior but does not declare any explicit tool scope or allowed-tools boundary. In an orchestration skill, undeclared shell capability is especially risky because it can normalize executing arbitrary user-defined chains without a visible permission contract, increasing the chance of unsafe command execution or unexpected filesystem modification.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation text uses broad trigger phrases such as requests to 'chain multiple scripts/commands' or 'orchestrate pipelines,' which can cause the skill to activate in many common contexts. For a skill that may execute shell commands and write files, overly broad activation increases the risk of accidental invocation and unsafe use without deliberate user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill describes running user-defined chain commands through the shell but does not provide a prominent warning about the risks of executing those commands. In this context, lack of warning is dangerous because users may treat the chain format as declarative configuration while it actually enables arbitrary shell execution, command injection, and filesystem or environment impact.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill adds a persistent self-learning and user-preference memory subsystem that is outside the stated orchestration scope. This creates a capability expansion risk: operational content and user-specific preferences can be retained and later reused in ways the user did not expect when invoking a workflow tool.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation introduces persistent learning and preference storage without a clear privacy, retention, or disclosure notice. This is risky because users may provide operational notes or preferences assuming they are ephemeral, when the skill instead stores them for future reuse in local memory files.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Persistent collection of user preferences, operational history, and notes is not necessary for a basic toolchain orchestrator and increases privacy and data-minimization risk. Because the retained data is natural-language and cumulative, it may capture sensitive project details, internal paths, failures, or user habits that can later be exposed or misused.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill explicitly instructs persistent recording of user preferences, error details, and operational notes for future reuse, creating a durable natural-language memory store. Such stores often accumulate sensitive context over time and can leak internal workflows, project names, file paths, failure causes, or user-specific habits if accessed by other components or users.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module’s natural-language documentation and command descriptions are entirely in Chinese, presenting the skill as generally applicable while not offering any language or locale opt-in. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale restriction is clearly justified.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The default data model persists preferences, error patterns, notes, and recent operations, which are unrelated to the stated DAG orchestration purpose. This creates unnecessary accumulation of user and operational metadata, increasing privacy risk and the blast radius if the file is accessed by other components or users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

record_preference writes user-supplied preferences directly to disk, and the broader module also stores notes and operational history, without any user-facing warning or consent flow. Because these fields may contain sensitive information, silent persistence can expose private data and create compliance issues.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The orchestrator executes chain-defined commands with subprocess.run(..., shell=True), and the command string is further modified by directly interpolating prior node stdout into later commands. This creates a command-injection path where a malicious or unexpected chain definition, or hostile upstream output, can execute arbitrary shell metacharacters and OS commands under the agent's privileges.

Content

Scanner excerpt · scripts/orchestrator.py (reported line 172)May include surrounding context.

python
for dep, val in outputs.items():
            cmd = cmd.replace(f"{{{{{dep}}}}}", (val or "").strip())
        print(f"▶ 运行 {k}: {cmd}")
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        stdout = r.stdout
        if n.get("capture") == "stdout":
            outputs[k] = stdout

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This skill is explicitly designed to run arbitrary shell commands from external chain definitions but provides no interactive confirmation, policy gate, or safety screening before executing potentially destructive operations. In an agent setting, that omission materially increases the chance that untrusted plans invoke file deletion, network exfiltration, package installation, or other dangerous side effects without user awareness.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring, CLI description, and user-facing guidance are all presented in Chinese, which imposes a specific language on users without any opt-in or explanation that the tool is intended only for a Chinese-speaking context. This matches the language/locale policy concern for natural-language content in code files.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.