T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/bridge-server.js:8
- Finding
- Unauthenticated and Cross-Origin Accessible Local POI API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bridge-server.js`, lines 8-79 **Vulnerability Type**: Missing authentication by default, overly permissive CORS, and unrestricted request-body handling **Risk Level**: High ### Vulnerable Code ```javascript const TOKEN = process.env.CLICKMAP_TOKEN || ''; ``` ```javascript function send(res, code, payload) { res.writeHead(code, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify(payload)); } function readBody(req) { return new Promise((resolve, reject) => { let raw = ''; req.on('data', chunk => (raw += chunk)); req.on('end', () => resolve(raw)); req.on('error', reject); }); } function authOk(req) { if (!TOKEN) return true; return req.headers['x-clickmap-token'] === TOKEN; } ``` ```javascript const server = http.createServer(async (req, res) => { if (req.method === 'OPTIONS') { res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, X-ClickMap-Token' }); return res.end(); } if (!authOk(req)) return send(res, 401, { ok: false, error: 'unauthorized' }); if (req.url === '/health' && req.method === 'GET') { return send(res, 200, { ok: true, service: 'clickmap-bridge', port: PORT, dataFile }); } if (req.url === '/api/pois' && req.method === 'GET') { return send(res, 200, { ok: true, ...loadData() }); } if (req.url === '/api/pois' && req.method === 'POST') { const raw = await readBody(req); let body; try { body = JSON.parse(raw || '{}'); } catch { return send(res, 400, { ok: false, error: 'invalid_json' }); } if (!Array.isArray(body.pois)) return send(res, 400, { ok: false, error: 'pois_array_required' }); const normalized = body.pois.map((p, idx) => ({ id: p.id || `poi-${Date.now()}-${idx}`, name: String(p.name || '').trim(), urlPa ...[truncated 4214 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require authentication by default** - Generate a cryptographically random token during setup. - Refuse to start the bridge when no token is configured. - Do not treat an empty token as authorization for every request. - Compare supplied tokens using a constant-time comparison. 2. **Restrict browser origins** - Replace `Access-Control-Allow-Origin: *` with an explicit allowlist. - Permit only the expected Chrome extension origin. - Validate the incoming `Origin` header before processing requests. - Return no CORS headers to untrusted origins. - Apply the same checks to preflight and normal requests. 3. **Consider a safer transport** - Prefer Chrome native messaging for communication between the extension and local process. - Alternatively, use an authenticated operating-system IPC mechanism rather than a generally accessible HTTP service. 4. **Limit request sizes** - Track accumulated byte length while receiving a request. - Reject requests exceeding a small documented limit with HTTP `413 Payload Too Large`. - Destroy or stop reading the request after the limit is exceeded. - Limit the number of POIs and the length of every string field. 5. **Validate POI records** - Enforce a strict schema for names, URLs, selectors, metadata, and coordinates. - Require finite numeric coordinates within reasonable bounds. - Reject unexpected fields and malformed nested objects. - Use atomic writes and preserve a recoverable backup before replacing the complete POI file. 6. **Reduce exposed information** - Avoid returning the absolute `dataFile` path in `/health` and write responses. - Return only the fields required by the requesting component. - Consider separating read and write permissions or endpoints. 7. **Harden automation integrity** - Require explicit user confirmation before importing or replacing all targets. - Bind POIs to expected origins and verify t ...[truncated 112 chars]
