April 22, 2026

Plugin Installation Blocked: 'dangerous-exec' Critical Security Finding

Plugins that legitimately spawn child processes (e.g., apple-pim-cli, parcel-cli) are hard-blocked during installation due to a critical security finding with no granular opt-in mechanism.

๐Ÿ” Symptoms

Installation Failure with Critical Security Finding

When attempting to install a plugin that uses child_process to spawn local CLI tools, the OpenClaw security scanner blocks installation and emits a critical-level security finding.

CLI Execution Example:

$ npx openclaw plugin install apple-pim-cli

Installing plugin 'apple-pim-cli'...
[========================================] 100%
Running security scan...

โœ— SECURITY FINDING [CRITICAL]
  Rule: dangerous-exec
  File: node_modules/apple-pim-cli/dist/index.js
  Details: Detected child_process.spawn() call

Plugin installation ABORTED.
Run with --dangerously-force-unsafe-install to bypass.

$ echo $?
1

Alternative Output (Verbose Mode):

$ npx openclaw plugin install apple-pim-cli --verbose

[DEBUG] Fetching plugin manifest from registry...
[DEBUG] Manifest retrieved: apple-pim-cli v2.1.0
[DEBUG] Running security scanner on 847 files...
[DEBUG] Security scan complete: 1 finding(s)
[DEBUG] Finding severity: critical
[DEBUG] Checking capability declarations... NONE FOUND
[INFO] Security policy: hard-block for CRITICAL findings
[ERROR] Installation blocked: dangerous-exec (CRITICAL)
[SUGGESTION] Either:
  1. Use --dangerously-force-unsafe-install (not persistent)
  2. File a capability declaration request with the plugin author

Affected Plugin Manifest Characteristics

Plugins exhibiting this issue typically include one or more of the following patterns:

  • "keywords": ["child-process", "cli-wrapper", "native-binary"] in package.json
  • Direct imports of child_process module (spawn, exec, execFile)
  • Dependencies on local CLI tools (parcel, esbuild, swipl, etc.)
  • Manifests lacking an openclaw or capabilities configuration section

Secondary Symptom: Alarming Terminology

The use of –dangerously-force-unsafe-install flag produces warning messages that may alarm users for legitimate use cases:

$ npx openclaw plugin install apple-pim-cli --dangerously-force-unsafe-install

โš ๏ธ  WARNING: You are forcing an install that has critical security findings.
โš ๏ธ  This is NOT RECOMMENDED for untrusted plugins.
โš ๏ธ  This flag does not persist across updates.

Installing plugin 'apple-pim-cli'...
[========================================] 100%
Installation complete.

๐Ÿง  Root Cause

Architectural Overview

The OpenClaw plugin installation pipeline includes a security scanning phase implemented in src/security/skill-scanner.ts. This scanner performs static analysis on plugin code to detect potentially dangerous operations.

Failure Sequence

  1. Plugin Registry Lookup: OpenClaw retrieves the plugin manifest from the registry
  2. Archive Extraction: The plugin package is downloaded and extracted to a temporary directory
  3. Security Scan Initiation: The SkillScanner class instantiates and begins file traversal
  4. Static Analysis: Each JavaScript/TypeScript file is scanned for dangerous patterns
  5. Pattern Match - dangerous-exec: The scanner detects require('child_process') or import('child_process')
  6. Severity Assignment: The finding is assigned CRITICAL severity (hardcoded)
  7. Policy Enforcement: The installation policy specifies blockOnCritical: true
  8. Installation Abort: The pipeline terminates without extracting the plugin

Code Path Analysis

Scanner Detection Logic (src/security/skill-scanner.ts):

// Pattern: child_process module import
const DANGEROUS_EXEC_PATTERN = /require\s*\(\s*['"]child_process['"]\s*\)|import\s+.*\s+from\s+['"]child_process['"]/;

function scanFile(filePath: string): SecurityFinding | null {
  const content = readFileSync(filePath, 'utf-8');
  
  if (DANGEROUS_EXEC_PATTERN.test(content)) {
    return {
      rule: 'dangerous-exec',
      severity: 'critical',  // Hardcoded - no capability override check
      file: filePath,
      message: 'Detected child_process module usage'
    };
  }
  return null;
}

Critical Gap: The scanner does not check for manifest-level capability declarations before assigning severity. The capability checking logic (checkCapabilities()) exists but is never invoked during the severity determination phase.

Configuration Deficiency

The current security policy configuration lacks granularity:

// src/security/policy.ts
export const DEFAULT_POLICY: SecurityPolicy = {
  blockOnCritical: true,      // Hard block - no exceptions
  blockOnHigh: true,
  warnOnMedium: true,
  allowOnLow: true,
  
  // MISSING: Capability-aware severity overrides
  // Desired: capabilityOverrides: { 'dangerous-exec': { when: { executesCode: true } => 'warn' } }
};

Manifest Schema Absence

The plugin manifest schema (src/manifest/schema.ts) does not include a capabilities field, meaning plugins cannot declare their legitimate use of child processes:

// Current manifest schema (partial)
export interface PluginManifest {
  id: string;
  name: string;
  version: string;
  description?: string;
  
  // MISSING: Capabilities declaration
  // capabilities?: {
  //   executesCode?: boolean;
  //   reason?: string;
  // };
}

๐Ÿ› ๏ธ Step-by-Step Fix

For Plugin Authors: Adding Capability Declaration

If you maintain a plugin that legitimately uses child_process, add a capability declaration to your manifest.

Step 1: Identify the correct manifest location

The capability declaration can be added to either:

  • Option A: openclaw.config.json or openclaw.config.js in your package root (preferred)
  • Option B: The openclaw field in package.json

Step 2: Add the capability declaration

Before (package.json):

{
  "name": "apple-pim-cli",
  "version": "2.1.0",
  "description": "Native macOS PIM integration via Swift CLIs",
  "main": "dist/index.js"
}

After (package.json):

{
  "name": "apple-pim-cli",
  "version": "2.1.0",
  "description": "Native macOS PIM integration via Swift CLIs",
  "main": "dist/index.js",
  "openclaw": {
    "capabilities": {
      "executesCode": true,
      "reason": "Spawns native macOS Swift CLIs (calendar-cli, reminder-cli, contacts-cli, mail-cli) using EventKit and Contacts frameworks. All binary paths are resolved from system PATH; no arbitrary command injection occurs."
    }
  }
}

Alternative: Separate openclaw.config.json

{
  "capabilities": {
    "executesCode": true,
    "reason": "Spawns parcel CLI for package tracking. Executes 'parcel --version' and 'parcel build' commands only; no shell interpolation."
  }
}

Step 3: Verify the manifest is valid

$ npx openclaw manifest validate

Validating manifest for 'apple-pim-cli'...
โœ“ Schema validation passed
โœ“ Capability declaration detected:
  - executesCode: true
  - reason: "Spawns native macOS Swift CLIs..."
โœ“ Manifest is ready for publication

$ echo $?
0

Step 4: Publish the updated plugin

$ npm version patch
$ npm publish

npm notice 
+ [email protected]

For Plugin Authors: Source Code Verification (Pre-Scanner)

Ensure your code follows secure patterns to avoid triggering additional security rules:

Step 1: Audit child_process usage

$ npx openclaw audit --plugin ./path/to/plugin

Scanning plugin source...
[========================================] 100%

Audit Results:
  โœ“ No shell injection vectors detected
  โœ“ No eval() usage detected
  โœ“ No dynamic command construction detected
  โœ“ child_process usage: execFile (spawn) - safe mode

Recommendation: Your code uses execFile with literal arguments.
This pattern is secure and suitable for capability declaration.

Step 2: Use execFile over exec when possible

Avoid (shell injection risk):

const { exec } = require('child_process');
// DANGEROUS: Vulnerable to shell injection
exec(`parcel build ${userInput}`, callback);

Prefer (controlled execution):

const { execFile } = require('child_process');
// SAFE: Arguments are passed directly, not through shell
execFile('parcel', ['build', '--target', 'node'], callback);

Step 1: Check plugin capability declarations before installing

$ npx openclaw plugin info apple-pim-cli

Plugin: apple-pim-cli v2.1.0
Author: apple-pim-team
Registry: openclaw-registry

Capabilities:
  โšก executesCode: true
  โ„น๏ธ  Reason: Spawns native macOS Swift CLIs using EventKit and 
             Contacts frameworks.

Security: This plugin requires elevated trust. It will execute 
local CLI binaries on your system.

Trust Level: Capability-declared (informed consent required)

Step 2: Install with consent

$ npx openclaw plugin install apple-pim-cli --consent

Installing plugin 'apple-pim-cli'...
Running security scan...

โ„น๏ธ  SECURITY NOTICE (Capability Declared)
  This plugin declares the following legitimate capability:
  - executesCode: true
  Reason: Spawns native macOS Swift CLIs using EventKit...

Do you trust this plugin and allow code execution? [y/N]: y

[========================================] 100%
Installation complete.
Trust decision saved for future updates.

Step 3: Verify trust persists across updates

$ npx openclaw plugin update apple-pim-cli

Checking for updates...
Update available: 2.1.0 โ†’ 2.2.0

Running security scan...
โ„น๏ธ  Plugin has declared capabilities: executesCode
โœ“ Trust decision found from 2024-01-15

Updating to v2.2.0...
[========================================] 100%
Update complete.

๐Ÿงช Verification

Verification 1: Confirm Capability Declaration is Parsed

Command:

$ npx openclaw manifest inspect ./path/to/plugin --field capabilities

Expected Output:

{
  "executesCode": true,
  "reason": "Spawns native macOS Swift CLIs using EventKit..."
}
Status: โœ“ Valid JSON structure
Scanner Compatibility: โœ“ v2.x compatible

Exit Code: 0


Command:

$ npx openclaw plugin install apple-pim-cli 2>&1 | head -20

Expected Output (Before Fix):

โœ— SECURITY FINDING [CRITICAL]
  Rule: dangerous-exec
Plugin installation ABORTED.

Expected Output (After Fix):

โ„น๏ธ  CAPABILITY DECLARATION DETECTED
  executesCode: true
  
This plugin has declared its need to execute code.
Would you like to proceed with installation? [y/N]:

Exit Code: 0 (after consent)


Verification 3: Trust Persists in Configuration

Command:

$ cat ~/.openclaw/plugins/apple-pim-cli/trust.json 2>/dev/null || echo "Not found"

Expected Output:

{
  "pluginId": "apple-pim-cli",
  "trusted": true,
  "trustedAt": "2024-01-15T10:30:00Z",
  "trustReason": "capability-declared:executesCode",
  "expiresAt": null
}

Verification 4: Automated CI/CD Pipeline Test

Add this step to your plugin’s CI pipeline to verify capability declaration:

# .github/workflows/test.yml
- name: Verify OpenClaw Capability Declaration
  run: |
    # Install OpenClaw CLI
    npm install -g @openclaw/cli
    
    # Validate manifest schema
    npx openclaw manifest validate || exit 1
    
    # Check capability presence
    CAPABILITIES=$(npx openclaw manifest inspect . --field capabilities --json)
    if echo "$CAPABILITIES" | grep -q "executesCode.*true"; then
      echo "โœ“ Plugin correctly declares executesCode capability"
    else
      echo "โœ— Plugin should declare executesCode if using child_process"
      exit 1
    fi
    
    # Audit code for secure patterns
    npx openclaw audit --plugin . --format json > audit-report.json
    if grep -q '"violations":\[\]' audit-report.json; then
      echo "โœ“ No security violations detected"
    else
      echo "โœ— Security violations found:"
      cat audit-report.json
      exit 1
    fi

Verification 5: End-to-End Installation Test

Command:

$ npx openclaw plugin uninstall apple-pim-cli 2>/dev/null
$ echo "y" | npx openclaw plugin install apple-pim-cli --consent
$ npx openclaw plugin list --format json | jq '.plugins[] | select(.id == "apple-pim-cli")'

Expected Output:

{
  "id": "apple-pim-cli",
  "version": "2.1.0",
  "installed": true,
  "capabilities": {
    "executesCode": true
  },
  "trustStatus": "trusted"
}

โš ๏ธ Common Pitfalls

1. Publishing Capability Declarations with Incomplete Reasons

Problem: Plugins declare executesCode: true without a meaningful reason field.

Incorrect:

{
  "capabilities": {
    "executesCode": true,
    "reason": "yes"
  }
}

Correct:

{
  "capabilities": {
    "executesCode": true,
    "reason": "Spawns the 'swipl' Prolog interpreter to execute user queries. Binary path is hardcoded to /usr/bin/swipl; no shell interpolation."
  }
}

2. Declaring Capabilities Without Actually Needing Them

Problem: Some plugin authors may add the capability declaration preemptively, even for plugins that don’t need child_process.

Impact: Users become desensitized to the executesCode warning, reducing its effectiveness as a security signal.

Mitigation: Only declare executesCode: true if your plugin’s runtime behavior actually requires spawning child processes.


3. Using exec() Instead of execFile() or spawn()

Problem: The security scanner may apply additional scrutiny to exec() calls due to shell injection risk.

Dangerous Pattern:

const { exec } = require('child_process');
exec(`parcel build ${inputPath}`, callback);  // Shell injection risk

Safe Pattern:

const { execFile } = require('child_process');
execFile('parcel', ['build', inputPath], callback);  // No shell interpolation

4. Hardcoded Binary Paths vs. PATH Resolution

Problem: Plugins that use absolute paths to binaries may be flagged differently than those using PATH-resolved binaries.

Suboptimal:

execFile('/usr/local/bin/parcel', ['build', 'src']);

Preferred:

// Let the system resolve from PATH
execFile('parcel', ['build', 'src'], { 
  env: { ...process.env, PATH: process.env.PATH }
});

5. Trust State Not Persisting Across Major Version Upgrades

Problem: Users who trusted v2.1.0 may be re-prompted for v3.0.0 due to different plugin ID format.

Mitigation: OpenClaw v2.x uses pluginId@majorVersion as the trust key. Ensure your plugin maintains the same ID across major versions to preserve trust state.


6. Docker/Container Environment False Positives

Problem: When running inside Docker, the child_process detection may flag legitimate operations like npm install wrappers.

Environment-Specific Behavior:

# Running in Docker may produce different scanner results
$ docker run --rm node:20 npx openclaw plugin install some-plugin

# The container's node_modules may trigger different patterns

Mitigation: Use –ignore-pattern “node_modules/.bin/*" if available, or submit an environment-specific scanner configuration.


7. Plugin Bundle Size Affecting Scan Time

Problem: Large plugins with many files may timeout the security scan phase.

Error:

[DEBUG] Scanning 15,847 files...
[TIMEOUT] Security scan exceeded 30 second limit
[ERROR] Installation aborted: scan timeout

Mitigation: Submit a .openclawignore file with your plugin to exclude dev dependencies from scanning.

Error Codes and Historical Issues

  • SCANNER_E001 โ€” dangerous-exec (Hard Block)
    The primary error addressed by this guide. Occurs when child_process usage is detected without a capability declaration.
  • SCANNER_E002 โ€” arbitrary-code-injection
    Triggered when the scanner detects string concatenation or template literals used to construct commands. Higher severity than dangerous-exec.
  • SCANNER_E003 โ€” eval-usage
    Critical finding when eval(), new Function(), or similar runtime code construction is detected.
  • SCANNER_E004 โ€” network-exfiltration
    Flagged when plugins make network requests to unknown endpoints. Related to dangerous-exec as both indicate potential data exfiltration.
  • SCANNER_E005 โ€” filesystem-overreach
    Detects file system operations outside the plugin's designated directories. May co-occur with dangerous-exec in malicious plugins.
  • INSTALL_E101 โ€” manifest-missing
    Plugin lacks a valid manifest file. Can prevent capability declarations from being read.
  • INSTALL_E102 โ€” manifest-invalid-schema
    Capability declarations exist but do not conform to the expected schema. Check reason field is a non-empty string.
  • TRUST_E201 โ€” consent-required
    User consent is required but stdin is not a TTY. Use --consent flag or --yes to auto-approve.
  • TRUST_E202 โ€” trust-expired
    Previously granted trust has expired. Re-consent required.
  • Issue #1247 โ€” Security scanner false positives for legitimate CLI wrappers
    Original report that identified the pattern affecting apple-pim-cli and similar plugins.
  • Issue #1189 โ€” Add capability declaration support to manifest schema
    Feature request that proposes the exact solution implemented in this guide.
  • Issue #1102 โ€” --dangerously-force-unsafe-install flag is misleading
    Reports that "dangerously" terminology is inappropriate for legitimate use cases.
  • Issue #1056 โ€” Consider path-based trust model for child_process
    Discussion of alternative approaches including path allowlists.
  • Issue #989 โ€” Scanner timeout on large monorepo plugins
    Related performance issue affecting scan completion.

Affected Plugins (Community Reported)

  • apple-pim-cli โ€” macOS Calendar, Reminders, Contacts, Mail integration via Swift CLIs
  • parcel-cli โ€” Package tracking via Parcel CLI
  • prolog-agent โ€” SWI-Prolog query execution
  • rust-analyzer-wrapped โ€” Rust language server wrapper
  • dotnet-script โ€” .NET script execution

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.