T09 · Insecure Skill Coding Practices
- Location
dashboard/lib/ralph-reader.mjs:175- Finding
Shell Command Injection Through Attacker-Controlled Loop State
- Content
View full analysis
/dev/null || true`, { encoding: 'utf8' }); const pids = result.trim().split('\n').filter(p => p && !killed.includes(p)); for (const pid of pids) { try { execSync(`kill ${pid} 2>/dev/null`); killed.push(pid); } catch (e) {} } } catch (e) {} } if (sessionId) { try { execSync(`pkill -f "${sessionId}" 2>/dev/null || true`); } catch (e) {} } ``` ### Technical Analysis The `sessionId` field is loaded from a JSON file under `/tmp` and interpolated directly into shell commands executed through `execSync`. No allowlist validation, shell escaping, or argument separation is applied. The active-loop scanner accepts every filename matching the broad `ralph-*.json` pattern: ```javascript const files = fs.readdirSync(TEMP_DIR) .filter(f => f.startsWith('ralph-') && f.endsWith('.json') && !f.includes('-done')); ``` Consequently, a local process, compromised autonomous agent, or other principal able to create a matching file in `/tmp` can place shell metacharacters in `sessionId`. When the dashboard kill endpoint is invoked, the value is interpreted by `/bin/sh` rather than passed solely as a literal process-search pattern. The same vulnerable value reaches both `pgrep` and `pkill`, providing two command-injection sinks. The loop identifier itself is also interpolated into a shell ...[truncated 1403 chars]- Remediation
View remediation
