T09 · Insecure Skill Coding Practices
- Location
auto-capture.js:83- Finding
OS Command Injection Through Attacker-Controlled Email Sender Data
- Content
View full analysis
Vulnerability Details
File Location:
auto-capture.js:83-108, with the untrusted source atauto-capture.js:284-287
Vulnerability Type: Shell command injection
Risk Level: CriticalVulnerable Code
js async function searchOkkiCustomer(email, companyName) { const domain = extractDomain(email); try { const { execSync } = require('child_process'); if (domain) { try { const result = execSync( `python3 "${CONFIG.vectorSearchPath}" search "${domain}"`, { encoding: 'utf8', timeout: 10000 } ); if (result && result.trim()) { return { source: 'domain', data: result }; } } catch (e) { // Ignore errors and continue } } if (companyName) { try { const result = execSync( `python3 "${CONFIG.vectorSearchPath}" search "${companyName}"`, { encoding: 'utf8', timeout: 10000 } ); if (result && result.trim()) { return { source: 'company', data: result }; } } catch (e) { // Ignore errors } } } catch (err) { return null; } }The values passed into this function originate from an untrusted email:
js const email = parsed.from?.value?.[0]?.address || ''; const companyName = parsed.from?.text?.replace(/<.*>/, '').trim() || ''; const okkiMatch = await searchOkkiCustomer(email, companyName);Technical Analysis
execSync()executes a string through a shell. AlthoughdomainandcompanyNameare placed inside double quotes, embedded quote characters, command substitutions, shell separators, or other shell metacharacters are not escaped.Both values are derived from the sender fields of an inbound email. Sender display names and address headers are controlled by the remote sender and must not be treated as safe command arguments.
For example, a crafted display name containing a sequence conceptually equivalent to:
text ...[truncated 1446 chars]- Remediation
View remediation
Remediation Suggestions
- Replace shell-string execution with an API that accepts an argument array:
js const { execFile } = require('child_process'); execFile( 'python3', [CONFIG.vectorSearchPath, 'search', domain], { encoding: 'utf8', timeout: 10000 }, callback );- Apply the same change to company-name searches.
- Validate sender-derived search terms:
- Enforce a conservative maximum length.
- Reject control characters, null bytes, and line breaks.
- Parse and validate domains using a dedicated hostname validator.
- Run the lookup subprocess under a restricted account without access to SMTP, IMAP, or unrelated filesystem credentials.
- Do not pass the complete parent environment to the lookup process unless required.
- Add regression tests containing quotes, semicolons, command substitutions, newlines, and shell redirection characters.
