Back to skill

Security audit

@openclaw/orchestration

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed local task-orchestration tool with some shared-use security caveats, but I found no hidden exfiltration, deception, or unrelated privileged behavior.

Install this only in environments where users and agents with access to the SQLite database are trusted to mutate the queue. Be careful with restore because it overwrites database state, update/pin dependencies before production use, and verify the sibling @openclaw/interchange implementation before relying on refresh output. Downstream agents should treat generated task markdown as untrusted task data.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/queue.js:65
Finding
Missing Agent Authentication and Task-Level Authorization<![CDATA[ ## Vulnerability Details **File Location**: `src/cli.js:67-108`; `src/queue.js:65-103`; `src/queue.js:107-188` **Vulnerability Type**: Missing authentication, agent impersonation, and broken task-level authorization **Risk Level**: High ### Vulnerable Code ```js task.command('claim <task-id>') .description('Claim a task') .requiredOption('--agent <name>', 'Agent name') .action((taskId, opts) => { const db = initDb(); try { const t = claimTask(db, taskId, opts.agent); if (t) console.log(`Task ${taskId} claimed by ${opts.agent}`); else console.error(`Failed to claim task ${taskId} (already taken or not pending)`); } catch (e) { console.error(e.message); } closeDb(); }); task.command('complete <task-id>') .description('Complete a task') .option('--result-path <path>', 'Path to result file') .option('--summary <text>', 'Summary of result') .action((taskId, opts) => { const db = initDb(); try { completeTask(db, taskId, { resultPath: opts.resultPath, summary: opts.summary }); console.log(`Task ${taskId} completed.`); } catch (e) { console.error(e.message); } closeDb(); }); task.command('fail <task-id>') .description('Fail a task') .option('--reason <text>', 'Reason for failure') .action((taskId, opts) => { const db = initDb(); try { failTask(db, taskId, opts.reason); console.log(`Task ${taskId} marked as failed.`); } catch (e) { console.error(e.message); } closeDb(); }); ``` ```js export function claimTask(db, taskId, agentName) { // Wrap everything in a transaction — no stale reads outside const doClaim = db.transaction(() => { const task = getTask(db, taskId); if (!task) throw new Error(`Task ${taskId} not found`); if (task.status !== 'pending') return null; // Check dependencies inside the transaction if (task.depends_on.length > 0) { const placeholders = task.depends_on.map(() => '?').join(','); co ...[truncated 4217 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce authenticated principals for all state-changing operations. Do not accept a plain agent name as proof of identity. 2. Require the verified actor identity in `claimTask`, `completeTask`, `failTask`, and `retryTask`. 3. Verify that the claiming agent exists before updating the task. 4. Enforce ownership for completion and failure: ```js if (task.assigned_agent !== actor.name && !actor.isAdmin) { throw new Error('Not authorized to modify this task'); } ``` 5. Define explicit administrative permissions for cross-agent task management and retry operations. 6. Store the verified principal—not caller-supplied display text—in `handoff_log`. 7. Enforce concurrency limits inside the same claim transaction, for example with a conditional agent update that requires `current_load < max_concurrent`, and reject the claim if no agent row is updated. 8. Restrict filesystem permissions on the SQLite database so untrusted local users cannot bypass application authorization through direct database access. 9. Add tests covering nonexistent agents, impersonation attempts, cross-agent completion/failure, unauthorized retry, and concurrent limit enforcement. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
src/protocol.js:12
Finding
Untrusted Task Content Is Published into Agent-Consumed Markdown<![CDATA[ ## Vulnerability Details **File Location**: `src/cli.js:20-36`; `src/protocol.js:12-41`; `src/interchange.js:142-152` **Vulnerability Type**: Stored prompt injection through the Markdown interchange channel **Risk Level**: Medium ### Vulnerable Code ```js task.command('create <title>') .description('Create a new task') .option('--desc <description>', 'Task description', '') .option('--priority <priority>', 'Priority: high/medium/low', 'medium') .option('--timeout <minutes>', 'Timeout in minutes', '60') .option('--depends-on <ids>', 'Comma-separated dependency task IDs', '') .option('--created-by <agent>', 'Creating agent', 'cli') .option('--max-retries <n>', 'Max retries', '3') .action((title, opts) => { const db = initDb(); const t = createTask(db, { title, description: opts.desc, priority: opts.priority, timeout: parseInt(opts.timeout, 10), dependsOn: opts.dependsOn ? opts.dependsOn.split(',').map(s => s.trim()) : [], createdBy: opts.createdBy, maxRetries: parseInt(opts.maxRetries, 10), }); console.log(`Created task ${t.id}: ${t.title} [${t.priority}]`); closeDb(); }); ``` ```js export function taskToMd(task, result) { const frontmatter = { skill: 'orchestration', type: 'detail', layer: 'state', task_id: task.id, status: task.status, assigned_to: task.assigned_agent || null, created_by: task.created_by, priority: task.priority, timeout_minutes: task.timeout_minutes, depends_on: Array.isArray(task.depends_on) ? task.depends_on : JSON.parse(task.depends_on || '[]'), version: 1, generator: 'orchestration@1.0.0', tags: ['task'], }; let content = `# Task: ${task.title}\n\n`; content += `## Description\n${task.description || 'No description provided.'}\n\n`; content += `## Constraints\n- Complete within ${task.timeout_minutes} minutes\n- Priority: ${task.priority}\n\n`; content += `## Result\n`; if (result) ...[truncated 3112 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Authenticate task creators and record verifiable provenance for each submitted task. 2. Clearly label title, description, summaries, and result paths as untrusted user-controlled data in generated documents. 3. Separate trusted operational instructions from task data using a structured interchange format with explicit trust metadata. 4. Require approval or policy validation before an agent executes newly submitted task content. 5. Configure consuming agents to treat interchange fields as data, never as authority to override system, developer, safety, or tool-use instructions. 6. Apply content screening for common prompt-injection patterns, while recognizing that filtering alone is not a complete defense. 7. Safely serialize Markdown and table fields to prevent structural document manipulation, including headings, links, HTML, and table delimiters. 8. Use allowlisted task schemas where possible rather than accepting unrestricted free-form operational instructions. 9. Add adversarial tests demonstrating that embedded instructions cannot override the consuming agent's trusted policy or execution constraints. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Ae1

High
Category
analysis-evasion
Content
node src/cli.js agent register my-agent --capabilities "coding,research"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli.js agent register my-agent --capabilities "coding,research"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli.js agent register my-agent --capabilities "coding,research"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli.js agent register my-agent --capabilities "coding,research"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node src/cli.js agent register my-agent --capabilities "coding,research"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The restore function performs a file write that can replace the active database at the default path, which is a potentially destructive operation. While there is a warning about WAL/SHM corruption risk, there is no disclosure or confirmation that the restore will overwrite the target database contents.

Context-Inappropriate Capability

Low
Confidence
95% confidence
Finding
The provided file is a Markdown code review, not an implementation file or manifest-backed skill definition. It contains prose about other source files and their behavior, but does not itself implement capabilities that can be compared against a stated purpose, so the intent/capability mismatch rules are not meaningfully applicable here.

Vague Triggers

Low
Confidence
76% confidence
Finding
This is a markdown file, so vague-trigger checks apply. The document identifies a review target and summarizes findings, but it provides no specific invocation scope, trigger examples, or exclusion conditions, which can make activation boundaries unclear if this file is used as skill-facing documentation.

Known Vulnerable Dependency: uuid==9.0.1 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
92% confidence
Finding
The lockfile pins uuid to 9.0.1, and the reported advisory describes a missing buffer bounds check in certain UUID generation paths when a caller supplies a buf argument. This is a real dependency risk, even though the package-lock.json alone does not prove the vulnerable API is exercised by this skill; if reachable, it could cause crashes or undefined behavior in consumers using those functions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=18"
  },
  "dependencies": {
    "better-sqlite3": "^11.0.0",
    "commander": "^12.0.0",
    "uuid": "^9.0.0"
  }
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "better-sqlite3": "^11.0.0",
    "commander": "^12.0.0",
    "uuid": "^9.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "better-sqlite3": "^11.0.0",
    "commander": "^12.0.0",
    "uuid": "^9.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Known Vulnerable Dependency: uuid==9.0.1 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The static analysis indicates resolution to uuid 9.0.1, which is affected by CVE-2026-41907 involving missing buffer bounds checks in certain UUID generation paths when a buf argument is supplied. If this package or downstream code invokes the vulnerable API shape, malformed input could trigger crashes or other unintended behavior, creating a dependency-level denial-of-service risk even though package.json alone does not prove exploitability.

Intent-Code Divergence

Low
Confidence
78% confidence
Finding
The module documentation says this is a 'Commander CLI entry point for the orchestration skill', which suggests a control interface for orchestration tasks. However, the same file imports and exposes backup and restore functionality, including full database restoration, which is a materially different administrative capability rather than just orchestration flow control.

Static analysis

No suspicious patterns detected.