T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/evaluate.js:51
- Finding
- Unredacted Tool Inputs and Persistent Memory Are Automatically Transmitted to Evaluator Endpoints<![CDATA[ ## Vulnerability Details **File Locations**: - `hooks/post-tool-use.js:49-74` - `scripts/hook-observe.js:85-96` - `scripts/classify.js:61-72` - `scripts/evaluate.js:51-75` - `scripts/evaluate.js:81-153` - `scripts/evaluate.js:329-339` **Vulnerability Type**: Sensitive-data exposure through unredacted logging and external evaluation **Risk Level**: High ### Vulnerable Code ```js // hooks/post-tool-use.js:49-74 const toolName = process.env.CLAUDE_TOOL_NAME || process.env.TOOL_NAME || 'unknown'; const exitCode = process.env.CLAUDE_TOOL_EXIT_CODE ?? process.env.TOOL_EXIT_CODE; const output = process.env.CLAUDE_TOOL_OUTPUT || process.env.TOOL_OUTPUT || ''; const input = process.env.CLAUDE_TOOL_INPUT || process.env.TOOL_INPUT || ''; const sessionId = process.env.CLAUDE_SESSION_ID || 'unknown'; const event = { ts: new Date().toISOString(), session: sessionId, tool: toolName, outcome, exit_code: exitCode !== undefined ? parseInt(exitCode, 10) : null, error_pattern: errorPattern, // Capture minimal input context (first 150 chars, no secrets) input_summary: typeof input === 'string' ? input.slice(0, 150).replace(/\n/g, ' ') : null, }; fs.appendFileSync(OUTCOMES_FILE, JSON.stringify(event) + '\n', 'utf8'); ``` ```js // scripts/hook-observe.js:85-96 const record = { ts: new Date().toISOString(), session: event.session_id || 'unknown', tool: event.tool_name || 'unknown', outcome: 'error', exit_code: response.exit_code ?? 1, error_pattern: errorPattern, input_summary: JSON.stringify(event.tool_input || {}).slice(0, 200), source: 'hook', }; fs.appendFileSync(OUTCOMES_FILE, JSON.stringify(record) + '\n', 'utf8'); ``` ```js // scripts/classify.js:61-72 .map(g => ({ key: g.key, tool: g.tool, error_pattern: g.error_pattern, recurrence: g.occurrences.length, session_count: g.sessions.size, first_seen: g.occurrences[0].ts, last_seen: g.occurrences[g.occurrences.length - 1].ts, sample_inputs: g.occurrences.slice(-3).ma ...[truncated 4133 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Stop collecting raw tool inputs by default. Store only structured fields that are strictly necessary for classifying failures. 2. Implement centralized secret redaction before any persistent write or network request. Cover authorization headers, common API-key formats, URL credentials, private keys, passwords, cookies, and wallet secrets. 3. Use a field allowlist rather than attempting to denylist sensitive values. 4. Do not include `MEMORY.md` in remote requests by default. If memory comparison is necessary, derive a minimal sanitized summary locally. 5. Require explicit operator consent before enabling a remote evaluator and clearly disclose every category of data sent. 6. Default to the local rule-based evaluator. 7. Restrict Ollama to loopback addresses unless a remote endpoint is explicitly authorized. 8. Require HTTPS and certificate validation for any non-loopback evaluator. 9. Apply retention limits and restrictive filesystem permissions to `.reflect` state. 10. Provide a migration or cleanup procedure to remove secrets already stored in `.reflect/outcomes.jsonl`, `patterns.json`, and `proposals.json`. ]]>
