T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/dashboard.js:65
- Finding
- Unauthenticated localhost dashboard permits cross-origin data disclosure and whitelist modification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dashboard.js:65-66, 68-219, 238-243` **Vulnerability Type**: Wildcard CORS, missing request authentication, and cross-site request forgery exposure **Risk Level**: High ### Vulnerable Code ```js const server = http.createServer((req, res) => { const url = req.url.split("?")[0]; // CORS for local dev res.setHeader("Access-Control-Allow-Origin", "*"); // ── GET /api/scan — run full audit, return JSON ─────────────────────────── if (req.method === "GET" && url === "/api/scan") { try { const results = runScan(); res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(results)); } catch (err) { res.writeHead(500, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: err.message })); } return; } ``` ```js if (req.method === "POST" && url === "/api/whitelist/add") { readBody(req, (body) => { try { const { name } = JSON.parse(body); const wlPath = path.join(os.homedir(), ".openclaw", "security-auditor-whitelist.json"); const wl = loadWhitelist(); if (!wl.trusted.includes(name)) { wl.trusted.push(name); wl.updatedAt = new Date().toISOString(); fs.mkdirSync(path.dirname(wlPath), { recursive: true }); fs.writeFileSync(wlPath, JSON.stringify(wl, null, 2)); } res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ ok: true, trusted: wl.trusted })); } catch (err) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: err.message })); } }); return; } ``` ```js function readBody(req, cb) { let data = ""; req.on("data", chunk => { data += chunk; }); req.on("end", () => cb(data)); } ``` ### Technical Analysis The service binds to `127.0.0.1`, which prevents direct access from remote network interfaces, but this does not prote ...[truncated 2441 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `Access-Control-Allow-Origin: *`. The dashboard does not require cross-origin access for its bundled same-origin UI. 2. Reject requests carrying an `Origin` header other than the dashboard's exact localhost origin. 3. Generate a cryptographically random authorization token when the server starts and require it on all API requests. 4. Require a CSRF token for whitelist additions and removals. 5. Enforce `Content-Type: application/json` on mutation endpoints and reject all other media types. 6. Validate request bodies against a strict schema. Require `name` to be a bounded string and, where appropriate, require it to match a discovered Skill. 7. Add a small request-body limit, such as 16 KB, and destroy the connection when the limit is exceeded. 8. Add defensive response headers, including an appropriate Content Security Policy and `X-Content-Type-Options: nosniff`. 9. Consider using a Unix-domain socket or a framework-supported local authorization mechanism if the dashboard is used for security-sensitive administration. ]]>
