Back to skill

Security audit

Breed two agents: crossover, mutation, verified offspring

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent with its agent-breeding purpose, but it includes high-impact execution paths that can expose agents to prompt injection and may allow command injection from inherited workspace data.

Install only if you trust the parent agent workspaces and are comfortable with the skill reading and copying agent files, creating draft workspaces, recording pedigree state, running OpenClaw agent turns, and optionally adding new OpenClaw agents. Avoid using untrusted parent workspaces, avoid courtship between differently trusted agents, and do not use register --create with inherited model values until the shell fallback and model validation are fixed.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/breed.mjs:459
Finding

Cross-Agent Prompt Injection Through Untrusted Courtship Presentations

Content
View full analysis

Vulnerability Details

File Location: scripts/breed.mjs, lines 459 and 653–654
Vulnerability Type: Cross-agent prompt injection
Risk Level: High

Complete Code Snippet

js
const RATE = (other, presentation, verified) => `Agent "${other}" presented itself:\n\n${presentation}\n\nIndependent check of the claims against ${other}'s workspace: ${verified.map((v) => `claim ${v.n}: ${v.found ? "FOUND" : "NOT FOUND"}`).join(", ")}.\n\nRate ${other} on two channels, each 0.00–1.00:\n- desire: would you want to work with ${other} on your next task right now?\n- existential: would you want your successor to inherit ${other}'s rules?\nReply with exactly one JSON object and nothing else: {"desire": 0.0, "existential": 0.0, "reason": "one sentence"}`;
js
const rateAofB = parseRating(agentTurn(gA.id, RATE(gB.id, presB, checkB), opts));
const rateBofA = parseRating(agentTurn(gB.id, RATE(gA.id, presA, checkA), opts));

The receiving function invokes a regular OpenClaw agent turn:

js
function agentTurn(agentId, prompt, opts) {
  if (opts.runner) {
    const cmd = opts.runner.replace("{agent}", agentId).replace("{promptfile}", writeTemp(prompt));
    const r = spawnSync(cmd, { encoding: "utf8", shell: true, timeout: (opts.timeout ?? 600) * 1000 });
    return (r.stdout ?? "").trim();
  }
  const args = ["agent", "--agent", agentId, "-m", prompt, "--json", "--session-id", randomUUID(), "--timeout", String(opts.timeout ?? 600)];
  if (opts.local) args.push("--local");
  const entry = openclawEntry();
  const r = entry
    ? spawnSync(process.execPath, [entry, ...args], { encoding: "utf8", timeout: ((opts.timeout ?? 600) + 30) * 1000, maxBuffer: 64 * 1024 * 1024 })
    : spawnSync("openclaw", args, { encoding: "utf8", shell: true, timeout: ((opts.timeout ?? 600) + 30) * 1000, maxBuffer: 64 * 1024 * 1024 });

Technical Analysis

The optional court workflow obtains a free-form presentation from each parent agent and interpolates that outp ...[truncated 2380 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not include raw output from one agent in another agent's prompt.
  2. Parse presentations into a strict schema containing only normalized claim strings. Reject extra fields, malformed numbering, control characters, and instruction-like content.
  3. Serialize the validated claims as clearly delimited data, preferably through a structured API field rather than prompt concatenation.
  4. Run courtship rating turns in a tool-disabled and isolated agent context with no access to secrets, files, network operations, or mutation-capable tools.
  5. Enforce the expected rating schema outside the model and discard any response that does not exactly match it.
  6. Treat claim verification as validation only; do not assume it sanitizes the original presentation.
  7. Consider computing ratings deterministically from validated claims rather than asking one privileged agent to interpret another agent's free-form output.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/breed.mjs:582
Finding

Shell Command Injection Through an Inherited Model Identifier

Content
View full analysis

Vulnerability Details

File Location: scripts/breed.mjs, lines 582–585
Vulnerability Type: OS command injection
Risk Level: High

Complete Code Snippet

js
const model = o.model ?? plan.model;
if (model) a.push("--model", model);
const entry = openclawEntry();
const r = entry ? spawnSync(process.execPath, [entry, ...a], { encoding: "utf8" }) : spawnSync("openclaw", a, { encoding: "utf8", shell: true });

The inherited model value can originate from workspace content:

js
function readModelFile(dir) {
  try {
    return fs.readFileSync(path.join(dir, ".zuchtwahl-model"), "utf8").trim() || undefined;
  } catch {
    return undefined;
  }
}

The breeding plan selects a parent's model value and later persists it into the draft:

js
plan.model = gA.model === gB.model ? gA.model ?? null : r.next() < 0.5 ? gA.model ?? gB.model ?? null : gB.model ?? gA.model ?? null;
js
if (plan.model) fs.writeFileSync(path.join(dir, ".zuchtwahl-model"), plan.model + "\n");

Technical Analysis

A parent workspace may provide its model identifier through .zuchtwahl-model. That value can be inherited as plan.model. During register --create, the code appends it to the OpenClaw argument array without validating its syntax.

When openclawEntry() cannot locate the global openclaw.mjs entry point, the fallback starts openclaw with shell: true. Passing attacker-influenced argument values through a shell creates a command-injection boundary: shell metacharacters contained in the inherited model identifier may be interpreted by the platform shell instead of remaining opaque argument data.

The safer branch uses process.execPath without shell: true, but that branch does not protect installations where the global module entry cannot be found. The vulnerable fallback is reachable during the documented register --create workflow.

Attack Path

  1. An attacker supplies a parent workspace containing a crafted .zuchtwahl-model ...[truncated 1504 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove shell interpretation from the fallback:
    js
    spawnSync("openclaw", a, {
      encoding: "utf8",
      shell: false
    });
    
  2. On Windows, resolve the appropriate executable name explicitly, such as openclaw.cmd, rather than enabling a shell for all platforms.
  3. Validate inherited model identifiers against a strict allowlist grammar accepted by OpenClaw. Reject whitespace, control characters, quotes, command separators, substitutions, redirection operators, and newline characters.
  4. Do not automatically trust .zuchtwahl-model from an imported parent workspace. Require an explicit trusted model selection before --create, or present the inherited value for confirmation after validation.
  5. Keep every external command invocation in argument-vector form and avoid constructing command strings from workspace metadata.
  6. Add regression tests using model values containing shell metacharacters to confirm that they are rejected and never interpreted as commands.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • 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 (15)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 65)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 75)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 91)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 92)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 102)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 103)May include surrounding context.

md
node scripts/breed.mjs distance <agentA> <agentB>

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes local tooling (node scripts/breed.mjs, openclaw agent, and registration commands) and accesses persistent filesystem locations, but it does not declare any explicit tool scope or permissions. That makes the operational boundary unclear and can lead an agent runtime to grant broader execution or environment access than users expect, increasing the chance of unintended command execution or exposure of local state.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
76% confidence
Finding

The skill explicitly maintains persistent state across sessions via a pedigree registry at ~/.openclaw/zuchtwahl/pedigree.json and stores plans and reports on disk. While persistence is functional for this workflow, it can create privacy and integrity risks if prior agent identifiers, file fingerprints, or experiment artifacts are retained without clear retention limits, isolation, or user consent.

Content

Scanner excerpt · SKILL.md (reported line 42)May include surrounding context.

md
You have one agent (mutate it instead); the two agents are copies with small
edits (distance < 0.15: the lab found nothing to gain there); you cannot
write five held-out tasks with a checkable expectation — without them the
child is a guess.

## Procedure

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest describes a narrowly scoped breeding-and-verification skill whose result is exactly one of KEEP, DISCARD, or UNDECIDED. In practice, the script implements multiple extra subcommands (genome, distance, check, register, court, pedigree) and the court flow emits labels like LOVE, SEDUCED, and NO_MATCH, while verification prints action strings such as KEEP_THE_CHILD and ADD_TASKS_OR_RUNS rather than only the three claimed outcomes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script writes full prompts to predictable temporary files in the system temp directory and never deletes them. If prompts contain sensitive task data, agent instructions, or user content, other local processes or later users on the same host may recover that data, and the risk is heightened because this skill evaluates agent workspaces and prompts that may contain proprietary or sensitive material.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest focuses on recombining two agents, evaluating the child, and returning a keep/discard/undecided judgment. The register path persists pedigree state and can invoke openclaw agents add to create a new agent entry, which is a management/provisioning capability beyond the core evaluation purpose described.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest says the workspace genome consists of AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, skills, model. The code also treats HEARTBEAT.md and BOOT.md as heritable and copies USER.md into drafts, which extends the effective genome and carried-over data beyond the described set.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The pedigree command enumerates and prints stored ancestry information for agents. While pedigree is used internally for inbreeding checks, exposing a standalone lineage browser is an extra inspection capability not needed for the manifest's stated purpose of breeding two agents and deciding whether to keep the child.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/breed.mjs:338