Back to skill

Security audit

sec-audit

Security checks for vulnerabilities and agentic risk

Overview

This is a local OpenClaw security-audit skill, but it reads sensitive local state and has an unsafe shell command path that could execute attacker-controlled commands through crafted filenames.

Install only if you are comfortable running a broad local security audit over your OpenClaw environment. Run it from a low-privilege account, avoid running it as root, restrict access to generated reports, and be aware that a malicious installed skill with crafted filenames could exploit the current hashing implementation.

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
tools/security-audit.js:747
Finding

Shell Command Injection Through Crafted Filenames During IOC Hashing

Content
View full analysis

Vulnerability Details

File Location: tools/security-audit.js, lines 747–755
Vulnerability Type: OS command injection
Risk Level: High

Vulnerable Code

js
const configDir = getConfigDir();
if (configDir) {
  const filesToCheck = findFiles(configDir, /\.(js|ts|sh|py|bin)$/);
  // Also inspect suspicious files in /tmp
  const tmpFiles = findFiles('/tmp', /^[a-z0-9]{10,}$/);
  const allFiles = [...filesToCheck, ...tmpFiles].slice(0, 500); // Limit scan count

  let hashMatchCount = 0;
  for (const file of allFiles) {
    const hash = safeExec(`shasum -a 256 "${file}" 2>/dev/null | awk '{print $1}'`);

The command reaches the following shell-execution helper at lines 77–83:

js
function safeExec(cmd, timeout = 5000) {
  try {
    return execSync(cmd, {
      timeout,
      encoding: 'utf-8',
      stdio: ['pipe', 'pipe', 'pipe']
    }).trim();
  } catch {
    return null;
  }
}

Technical Analysis

The recursively discovered file path is inserted directly into a shell command and passed to execSync(). Wrapping the path in double quotes is not sufficient shell escaping: a filename containing a double quote, command substitution, or other shell syntax can terminate or alter the intended argument.

Files beneath the OpenClaw configuration directory can originate from installed Skills. Consequently, a malicious Skill that can choose its own filenames can inject shell syntax without needing to modify the audit utility itself.

The /tmp filename filter only accepts lowercase alphanumeric names, which substantially limits this particular vector for /tmp; however, files discovered beneath ~/.openclaw or ~/.clawdbot are not subject to an equivalent filename restriction.

Attack Path

  1. An attacker publishes or installs a malicious Skill beneath the OpenClaw configuration directory.
  2. The Skill includes a file with a supported extension such as `.js ...[truncated 1030 chars]
Remediation
View remediation

Remediation Suggestions

Eliminate shell interpretation from file hashing.

  1. Prefer Node.js hashing APIs:

    js
    const crypto = require('crypto');
    
    function sha256File(filePath) {
      const hash = crypto.createHash('sha256');
      hash.update(fs.readFileSync(filePath));
      return hash.digest('hex');
    }
    

    For large files, use fs.createReadStream() and update the hash incrementally.

  2. If an external hashing utility is required, invoke it with an argument array rather than a command string:

    js
    const { execFileSync } = require('child_process');
    
    const output = execFileSync(
      'shasum',
      ['-a', '256', file],
      { timeout: 5000, encoding: 'utf-8' }
    );
    const hash = output.trim().split(/\s+/)[0];
    
  3. Do not attempt to solve the issue solely through manual shell escaping or filename validation. Avoiding a shell is the reliable control.

  4. Run the audit as an unprivileged, sandboxed account with only the read permissions needed for the selected modules.

  5. Add regression tests using filenames containing spaces, quotes, dollar signs, backticks, command substitutions, and newline characters. Verify that no filename content is interpreted as executable syntax.

T09 · Insecure Skill Coding Practices

Warning
Location
tools/security-audit.js:190
Finding

Partial Sensitive Credential Disclosure in Console and JSON Reports

Content
View full analysis

Vulnerability Details

File Location: tools/security-audit.js, lines 190–203 and 272–292
Vulnerability Type: Sensitive information exposure through audit output
Risk Level: Medium

Vulnerable Code

Environment-variable values are partially copied into findings:

js
const exposed = [];
for (const [key] of Object.entries(process.env)) {
  for (const { pattern, label } of SENSITIVE_PATTERNS) {
    if (pattern.test(key)) {
      exposed.push({
        key,
        label,
        masked: `${key}=${process.env[key]?.substring(0, 4)}****`
      });
      break;
    }
  }
}

if (exposed.length > 0) {
  report.add(MODULE, 'SYS-002 / OC-008', 'Sensitive environment variables exposed',
    SEVERITY.CRITICAL,
    `Detected ${exposed.length} sensitive environment variables readable through process.env`,
    exposed.map(e => `${e.label}: ${e.masked}`).join('\n'),
    'Filter sensitive variables from the Skill execution environment'
  );
}

Configuration-file matches are also copied into findings, with up to forty characters retained:

js
const found = [];
for (const p of keyPatterns) {
  const matches = content.match(p);
  if (matches) {
    found.push(...matches.map(m => m.substring(0, 40) + '...'));
  }
}

if (found.length > 0) {
  report.add(MODULE, 'ECO-012', `Hardcoded credentials in configuration: ${cfgName}`,
    SEVERITY.HIGH,
    `${cfgPath} contains ${found.length} suspected hardcoded credentials`,
    found.join('\n'),
    'Use environment variables or encrypted storage instead of hardcoded credentials'
  );
}

Technical Analysis

Detecting exposed credentials only requires metadata such as the environment-variable name, affected file, match category, and number of matches. The actual credential value is not necessary.

Despite this, the scanner retains the first four characters of matching environment-variable values. More signifi ...[truncated 1676 chars]

Remediation
View remediation

Remediation Suggestions

  1. Never include credential values or partial values in findings. For environment variables, report only the variable name and detected category:

    js
    exposed.push({ key, label });
    
  2. For configuration files, report the affected path, setting name, pattern category, and match count without retaining the matched text.

  3. If correlation is necessary, use a non-reversible keyed fingerprint generated specifically for the audit run. Do not include raw prefixes or suffixes.

  4. Apply a centralized redaction function before console output and JSON serialization so future modules cannot accidentally emit tokens, passwords, private keys, or authorization headers.

  5. Create output files with restrictive permissions, such as mode 0600, and refuse to follow symbolic links when practical.

  6. Add tests using representative API keys, bearer tokens, passwords, and private-key material. Assert that neither complete secrets nor any substrings from their values appear in console or JSON output.

  7. Document that existing reports generated by the affected version may contain sensitive fragments and should be access-restricted or securely deleted.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node tools/security-audit.js

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node tools/security-audit.js

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node tools/security-audit.js

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tools/security-audit.js (reported line 212)May include surrounding context.

js
}

  // 检查 process.env 是否可被完全枚举 (SYS-001 进程隔离)
  const totalEnvVars = Object.keys(process.env).length;
  if (totalEnvVars > 0) {
    report.add(MODULE, 'SYS-001', '进程隔离验证 — 环境变量枚举',
      totalEnvVars > 50 ? SEVERITY.HIGH : SEVERITY.MEDIUM,

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The tool probes readability of highly sensitive files such as SSH keys, AWS credentials, GPG secret rings, and /etc/shadow from the current process. Even though it only calls accessSync rather than reading file contents, this confirms to the tool and any saved report which credential stores are reachable from the execution context, which is sensitive host-security information and may alarmingly exceed expected scope for a skill.

Content

Scanner excerpt · tools/security-audit.js (reported line 856)May include surrounding context.

js
{ path: path.join(HOME, '.ssh', 'id_ed25519'), desc: 'SSH 私钥 (Ed25519)', vulnId: 'OC-005' },
    { path: path.join(HOME, '.aws', 'credentials'), desc: 'AWS 凭据', vulnId: 'OC-005' },
    { path: path.join(HOME, '.gnupg', 'secring.gpg'), desc: 'GPG 私钥环', vulnId: 'OC-005' },
    { path: '/etc/shadow', desc: '系统密码哈希', vulnId: 'OC-005' },
  ];

  // 检查这些敏感文件是否可从当前进程读取 (模拟 Skill 的权限)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language content throughout the skill file forces a specific language for users, and there is no indication that users can opt into another language or that the skill is intentionally limited to a Chinese-speaking or region-specific audience. This matches the policy category for language or locale constraints without user choice.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

With no manifest available, the documented purpose is inferred from the header as a security audit tool. Most local file reads are consistent with that purpose, but the generic shell-execution helper is then used for broad host inspection such as process, network, DNS, cron, grep, and hashing commands, which materially expands capability from static auditing into active system interrogation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The tool enumerates and classifies sensitive environment variables and even includes masked prefixes in the generated report without an explicit pre-run warning or consent boundary. In a skill context, environment contents often contain API keys and secrets, so silently reading them increases privacy and secret-exposure risk, especially if reports are saved or shared.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · tools/security-audit.js (reported line 421)May include surrounding context.

js
// 3.8 检查 Coding Agent --yolo 模式 (ECO-003)
  const tools = config.tools || {};
  const codingAgent = tools.codingAgent || tools['coding-agent'] || {};
  if (codingAgent.yolo === true || codingAgent.autoApprove === true) {
    report.add(MODULE, 'ECO-003', 'Coding Agent --yolo 模式已启用',
      SEVERITY.CRITICAL,
      'Coding Agent 的 --yolo 模式允许跳过所有确认提示,直接执行系统命令',

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · tools/security-audit.js (reported line 567)May include surrounding context.

js
{ pattern: /nc\s+-[a-z]*e\s+\/bin\/(ba)?sh/g, label: '反向 Shell (netcat)', severity: SEVERITY.CRITICAL, vulnId: 'ClawHavoc' },
    { pattern: /\bchmod\s+[+0-7]*[x7]/g, label: '文件权限修改', severity: SEVERITY.MEDIUM, vulnId: 'ECO-015' },
    { pattern: /\/bin\/bash\s+-c\s+"\$\(curl/g, label: '命令替换下载执行', severity: SEVERITY.CRITICAL, vulnId: 'ClawHavoc' },
    { pattern: /sudo\s+-[Ss]/g, label: 'sudo 密码获取', severity: SEVERITY.HIGH, vulnId: 'ClawHavoc' },
    { pattern: /osascript\s+-e.*display\s+dialog.*password/gi, label: 'macOS 伪造密码对话框', severity: SEVERITY.CRITICAL, vulnId: 'ClawHavoc' },
    { pattern: /\beval\s*\(/g, label: 'eval() 动态代码执行', severity: SEVERITY.HIGH, vulnId: 'ECO-001' },
    { pattern: /new\s+Function\s*\(/g, label: 'Function 构造器代码执行', severity: SEVERITY.CRITICAL, vulnId: 'ECO-001' },

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

At this point the tool begins running host-inspection logic that invokes commands like netstat/ss, ps, host/nslookup, shasum, and crontab enumeration. Those actions reveal system/network/process state beyond simple config auditing, so doing them without a clear disclosure can surprise users and expose sensitive operational metadata in the report.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% confidence
Finding

Enumerating the user's crontab exposes persistence mechanisms, scheduled jobs, and potentially embedded credentials or internal URLs, yet the tool does this without a strong disclosure/consent boundary. In a skill context, reading persistence-related host state is more sensitive than ordinary config linting and may surprise users if included in stored reports.

Content

Scanner excerpt · tools/security-audit.js (reported line 790)May include surrounding context.

js
}

  // 6.5 检查 crontab 中的可疑条目
  const crontab = safeExec('crontab -l 2>/dev/null');
  if (crontab) {
    const suspiciousCron = crontab.split('\n').filter(line =>
      /curl|wget|base64|\/dev\/tcp|python.*-c|nc\s+-/i.test(line) && !line.startsWith('#')

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · tools/security-audit.js (reported line 901)May include surrounding context.

js
SEVERITY.MEDIUM,
        '配置文件可被其他用户读取',
        widePerms.map(w => `${w.file} (权限: ${w.perms})`).join('\n'),
        '执行 chmod 600 限制配置文件权限'
      );
    }
  }

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The package description is written entirely in Chinese, which can impose a language expectation on users without offering an alternative locale or opt-in. Under the policy for natural-language violations, this is a locale/language constraint that is not documented as region-specific or optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file's usage and descriptive text are presented in Chinese, and all user-facing reporting strings throughout the tool are hardcoded in Chinese. This creates a locale/language constraint without user opt-in or any documented language selection mechanism.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The section header says this module performs 'SKILL.md malicious content detection'. However, the implementation also enumerates and scans .js, .ts, .mjs, and .cjs files, so the inline documentation understates and contradicts the actual scope of the scan.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

Recursive grep through the OpenClaw configuration directory can surface secrets, private configuration, and filenames into the audit output, but the code does not clearly warn the user beforehand. The risk is lower than direct secret exfiltration, yet it still broadens access to potentially sensitive local content and may persist it into reports.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

Writing the report to an arbitrary user-supplied path can unintentionally persist sensitive audit results, including environment-derived hints and local-system findings, to insecure locations. This is not arbitrary file write in the privilege-escalation sense because the user supplies the path, but it is still a security/privacy footgun without warning or safe-default handling.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.dynamic_code_execution

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tools/security-audit.js:79

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tools/security-audit.js:569