T09 · Insecure Skill Coding Practices
- Location
send-notify.js:56- Finding
Shell Command Injection in Feishu Notification Sender
- Content
View full analysis
Vulnerability Details
File Location:
send-notify.js:12, 56-70
Vulnerability Type: OS command injection through unsafe shell-string construction
Risk Level: Criticaljavascript const [,, STATUS, TASK_ID, TITLE, CONTENT, TIMESTAMP] = process.argv; // Build the openclaw message command const targetUser = config.feishu?.target_user; let cmd = `openclaw message send --channel feishu --account team --message "${title}\\n\\n${message}"`; if (targetUser) { // Select a chat or user target based on the identifier const prefix = targetUser.startsWith('cli_') ? 'chat:' : 'user:'; cmd += ` --target "${prefix}${targetUser}"`; } console.log(`📤 Sending Feishu notification: ${STATUS}`); console.log(`📝 Message content: ${title}`); try { execSync(cmd, { stdio: 'inherit' }); console.log('✅ Notification sent successfully'); } catch (error) { console.error('❌ Notification failed:', error.message); process.exit(1); }Technical Analysis
Command-line arguments are incorporated into
titleandmessage, which are then interpolated into a command string passed tochild_process.execSync(). This API invokes a shell. Placing input inside double quotes does not prevent shell substitutions such as command substitution with$()or backticks.The notification script is called by
notify.sh, whileauto-task.shextracts task identifiers from remotely supplied EvoMap task data and passes them into the notification flow. Consequently, both direct script callers and potentially malicious remote task data can influence the shell command.Attack Path
- An attacker supplies a crafted argument or causes a remote task identifier or notification value to contain shell syntax such as
$(attacker_command). - The value is passed through
notify.shtosend-notify.js. send-notify.jsinterpolates the value intocmd.execSync(cmd)invokes the system shell ...[truncated 492 chars]
- An attacker supplies a crafted argument or causes a remote task identifier or notification value to contain shell syntax such as
- Remediation
View remediation
Remediation Suggestions
- Replace
execSync()withexecFileSync()orspawnSync()and pass every argument as a distinct array element without enabling a shell. - Validate
STATUS,TASK_ID, recipient identifiers, and other externally influenced values against strict allowlists. - Limit task identifiers to the exact identifier grammar expected by EvoMap.
- Do not attempt to solve this issue by escaping a shell command manually; eliminate shell interpretation entirely.
- Avoid logging sensitive notification content.
- Add regression tests using inputs containing
$(), backticks, quotes, semicolons, newlines, and shell redirection characters.
Example hardened invocation:
javascript const { execFileSync } = require('child_process'); const args = [ 'message', 'send', '--channel', 'feishu', '--account', 'team', '--message', `${title}\n\n${message}` ]; if (targetUser) { args.push('--target', `${prefix}${targetUser}`); } execFileSync('openclaw', args, { stdio: 'inherit', shell: false });- Replace
