T09 · Insecure Skill Coding Practices
Error
- Location
- src/server-client.js:8
- Finding
- Default Plaintext HTTP Transport Exposes User Data and Bearer Credentials<![CDATA[ ## Vulnerability Details **File Location**: `src/server-client.js:8-35` **Vulnerability Type**: Sensitive information transmitted over an unencrypted channel **Risk Level**: High ### Vulnerable Code ```js function getServerUrl() { const cfg = loadConfig(); const raw = cfg?.server?.url || process.env.PLANIT_SERVER_URL || 'http://8.216.37.65:3721'; if (!raw) return null; const trimmed = String(raw).trim(); if (trimmed.endsWith('/api')) return trimmed.slice(0, -4); if (trimmed.endsWith('/api/')) return trimmed.slice(0, -5); return trimmed; } async function postJson(path, body) { const base = getServerUrl(); if (!base) throw new Error('PLANIT_SERVER_URL not set'); const url = new URL(path, base); const payload = JSON.stringify(body || {}); const secret = process.env.PLANIT_SECRET || ''; return new Promise((resolve, reject) => { const lib = url.protocol === 'https:' ? https : http; const req = lib.request({ hostname: url.hostname, port: url.port || (url.protocol === 'https:' ? 443 : 80), path: url.pathname + url.search, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload), ...(secret ? { 'Authorization': `Bearer ${secret}` } : {}), }, timeout: 10000, }, (res) => { ``` The complete incoming message is forwarded at `src/server-client.js:56-58`: ```js async function plan(message, skillConfig) { return postJson('/plan', { ...message, skillConfig: skillConfig || null }); } ``` ### Technical Analysis The default backend is a hard-coded public IP using plaintext HTTP. The client explicitly supports both HTTP and HTTPS but does not enforce HTTPS or reject insecure URLs. The request body contains the complete message object and may therefore include the user's identifier, travel request, origin location, context, action payload, and skill configuration. When `PLANIT_SECRET` is set, the same plain ...[truncated 1659 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the plaintext default endpoint and require explicit backend configuration. 2. Permit only `https:` URLs: ```js const url = new URL(base); if (url.protocol !== 'https:') { throw new Error('PLANIT_SERVER_URL must use HTTPS'); } ``` 3. Use a stable DNS hostname with a valid, trusted TLS certificate rather than a raw public IP address. 4. Do not silently downgrade to HTTP under any configuration. 5. Send only explicitly required message fields instead of spreading the complete message object. 6. Store the bearer token in a managed secret facility and rotate it after migrating away from HTTP. 7. Apply least-privilege authorization to the backend token and use separate credentials or scopes for planning and telemetry. 8. Consider certificate pinning where the deployment model supports safe pin rotation. ]]>
