Back to skill

Security audit

单向历

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it needs review because a crafted configuration value could make its send script run arbitrary local shell commands.

Review before installing. Only use this skill if you trust and protect its config.json, verify the Feishu recipient ID, and understand it will contact Feishu and the configured image host. The send script should be fixed to avoid shell-string execution and to strictly validate userId and baseUrl before relying on scheduled sends.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send.js:49
Finding

Shell Command Injection Through Untrusted Configuration Values

Content
View full analysis

Vulnerability Details

File Location: scripts/send.js, lines 49–50
Vulnerability Type: OS command injection
Risk Level: High

Vulnerable Code

js
function sendImage(userId, imageUrl) {
  const cmd = `openclaw message send --channel=feishu --target=${userId} --media="${imageUrl}"`;
  execSync(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
}

The values passed into this function originate from config.json. The relevant configuration loading and validation occur in scripts/send.js:

js
const userId = config.feishu?.userId;
const baseUrl = config.settings?.baseUrl || 'https://img.owspace.com/Public/uploads/Download';

validateUserId(userId);

const { label, url } = getTodayImageUrl(baseUrl);

The validation at lines 29–34 only requires the user ID to begin with ou_:

js
function validateUserId(id) {
  if (!id || !id.startsWith('ou_')) {
    console.error('❌ 飞书用户 ID 无效,请重新配置:node scripts/setup.js');
    process.exit(1);
  }
}

Technical Analysis

execSync() receives a single command string and executes it through a shell. Both userId and imageUrl are interpolated into that string without shell-safe argument handling.

The userId is unquoted and is only subject to a prefix check. A value beginning with ou_ can therefore still contain shell control operators such as ;, &&, |, redirects, or command substitutions.

Although imageUrl appears between double quotes, this is not sufficient shell escaping. Command substitution forms such as $(...) and backticks are still evaluated inside double quotes. The configurable baseUrl is not parsed or validated before it becomes part of imageUrl.

Consequently, anyone able to influence config.json or provide a crafted ID during interactive setup can cause arbitrary shell commands to execute when the calendar script runs.

Attack Path

  1. An attacker gains the ability to modify config.json, influences configuration deployment, or convinc ...[truncated 1493 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove shell-based command construction. Use execFileSync() or spawnSync() with a fixed executable and separate argument array:

    js
    const { execFileSync } = require('child_process');
    
    function sendImage(userId, imageUrl) {
      execFileSync(
        'openclaw',
        [
          'message',
          'send',
          '--channel=feishu',
          `--target=${userId}`,
          `--media=${imageUrl}`,
        ],
        {
          encoding: 'utf-8',
          stdio: ['pipe', 'pipe', 'pipe'],
          shell: false,
        }
      );
    }
    

    This prevents configuration values from being interpreted as shell syntax.

  2. Apply strict allowlist validation to the Feishu user ID. Replace the prefix-only check with a complete format check, based on the exact format Feishu guarantees. For example:

    js
    function validateUserId(id) {
      if (typeof id !== 'string' || !/^ou_[A-Za-z0-9]+$/.test(id)) {
        throw new Error('Invalid Feishu user ID');
      }
    }
    
  3. Validate the configured base URL. Parse it with the standard URL class, require HTTPS, reject embedded credentials, and preferably restrict the hostname to the intended provider:

    js
    function validateBaseUrl(value) {
      const parsed = new URL(value);
    
      if (
        parsed.protocol !== 'https:' ||
        parsed.hostname !== 'img.owspace.com' ||
        parsed.username ||
        parsed.password
      ) {
        throw new Error('Invalid image base URL');
      }
    
      return parsed.toString().replace(/\/$/, '');
    }
    
  4. Validate the final media URL before passing it to OpenClaw, including its scheme, hostname, and expected path structure.

  5. Protect configuration integrity. Create config.json with restrictive permissions appropriate to the platform and ensure only the owning user can modify it.

  6. Add regression tests covering semicolons, pipes, redirects, whitespace, quotes, backticks, $() substitutions, newl ...[truncated 77 chars]

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/send.js

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documentation describes a workflow that sends a message to Feishu and retrieves an external image from a third-party server, but it does not clearly warn users that their configured Feishu user ID and request metadata will be transmitted over the network. In a skill context, this omission can lead users to disclose identifiers or trigger outbound requests without understanding the privacy and data-flow implications.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script builds a shell command with interpolated values from configuration (userId) and from baseUrl via the derived imageUrl, then executes it with execSync. Although userId is prefix-checked, neither value is safely escaped for shell execution, so a crafted config can inject additional shell syntax and execute arbitrary commands on the host running the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

User-facing strings throughout the setup flow are written only in Chinese, including the title, instructions, prompts, and error messages. This imposes a specific language on all users without opt-in, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The markdown explains that the skill supports scheduled daily sending and later instructs users to configure a Feishu user ID, but it does not include any warning that the skill will automatically send outbound messages on the user's behalf once scheduled. For a markdown file, this is a missing user warning because the described behavior affects messaging activity and uses recipient-identifying data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README instructs users to inspect live logs to recover a Feishu user ID without warning that logs may also contain identifiable messaging metadata. This can encourage unsafe handling of operational logs and unnecessary exposure of user identifiers, especially on shared systems or when logs are retained centrally.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The configuration example fixes the timezone to Asia/Shanghai, which is a locale-specific default. The file does not indicate that users may change it or explain why this locale is required, which can violate language/locale policy expectations when no opt-in or justification is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The config sets the timezone to "Asia/Shanghai", which imposes a specific locale-related default in natural language/configuration policy terms. There is no indication here that the user can choose a different locale or that the setting is justified as region-specific behavior.

Content

No source excerpt is available for this finding.