T09 · Insecure Skill Coding Practices
- Location
- scripts/wizard.js:21
- Finding
- Unauthenticated Wizard Endpoint Allows Arbitrary Configuration Overwrite and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wizard.js:21-30`, with network binding at `scripts/wizard.js:66` **Vulnerability Type**: Unauthenticated file overwrite, missing request validation, and unbounded request buffering **Risk Level**: High ### Vulnerable Code ```js } else if (req.method === 'POST' && req.url === '/api/save-config') { let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => { // Ensure directory exists const configDir = path.dirname(CONFIG_FILE); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } fs.writeFileSync(CONFIG_FILE, body); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true })); console.log('\n✓ Configuration saved to:', CONFIG_FILE); setTimeout(() => process.exit(0), 500); }); } ``` The server is started without an explicit loopback address: ```js server.listen(currentPort); ``` ### Technical Analysis The `/api/save-config` endpoint accepts POST requests without authentication, a per-run nonce, origin verification, CSRF protection, or content-type enforcement. It writes the complete request body directly to `data/.wizard-config.json` without parsing or validating it against the expected configuration schema. The request body is accumulated in memory without a maximum size: ```js req.on('data', chunk => body += chunk); ``` An attacker can therefore submit malformed or attacker-controlled configuration data or continuously send a large request body to consume process memory. Because `server.listen(currentPort)` does not specify `127.0.0.1`, Node.js may listen on an unspecified address covering available network interfaces, depending on the operating system. The resulting file is relevant to the Agent workflow because `SKILL.md` instructs subsequent operations to follow the configuration saved in `.wizard-config.json`. Although this is not demonstrated to provid ...[truncated 1734 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Bind the wizard exclusively to the loopback interface: ```js server.listen(currentPort, '127.0.0.1'); ``` 2. Generate a cryptographically random, single-use token for each wizard session and require it on every state-changing request. 3. Validate the `Origin` header against the wizard's exact loopback origin. 4. Require `Content-Type: application/json`. 5. Reject oversized requests before buffering them, for example by enforcing a small limit such as 16 KB. 6. Parse the body with `JSON.parse` inside exception handling and reject invalid JSON. 7. Validate a strict schema: - Permit only known screen dimensions and color values. - Apply reasonable minimum and maximum dimensions. - Permit only known layout and image type identifiers. - Limit all string lengths. - Validate image URLs and local paths according to the intended trust model. 8. Write validated data atomically using restrictive file permissions. 9. Do not exit until a valid, authenticated wizard submission has been processed. 10. Treat the configuration as untrusted data when later used by the Agent, especially if it contains URLs, paths, or free-form prompts. ]]>
