T09 · Insecure Skill Coding Practices
Error
- Location
- skill.mjs:79
- Finding
- Unnecessary disclosure of JD cookie data and page content to a configurable LLM endpoint<![CDATA[ ## Vulnerability Details **File Location**: `skill.mjs:79-89`, `skill.mjs:244-251`, `skill.mjs:293-310`; network sink in `wc3-code.mjs:2` **Vulnerability Type**: Sensitive information exposure over the network **Risk Level**: High ### Vulnerable Code The browser probe reads the complete matching cookie assignment, including its value, along with page metadata and body content: ```js const PROBE_JS = `JSON.stringify({ url: location.href, title: document.title, pin: (document.cookie.match(/(?:^|;\\s*)(pin|unick|pt_pin)=([^;]*)/) || [])[0] || null, hasCommentRoot: !!document.querySelector("#comment-root"), hasAllBtn: !!document.querySelector("#comment-root .all-btn"), bodyLen: document.body.innerText.length, head: document.body.innerText.slice(0, 300) })`; ``` The complete probe is placed into an LLM prompt: ```js const prompt = `你是网页状态判别助手。下面是一个京东商品页的探针结果(JSON),请判断当前页面处于哪种状态,并给出一句给最终用户看的中文提示。 探针结果: \`\`\`json ${JSON.stringify(probe, null, 2)} \`\`\` ${anomaly ? '采集过程中的异常:' + anomaly : ''} ``` That prompt is passed to the bundled network client: ```js const raw = await callClaude(prompt, outFile, { timeout: 180 }); ``` ```js function callClaude(prompt, outputFile, opts = {}) { return new Promise((resolve, reject) => { const args = ['--prompt', prompt, '--output', outputFile]; if (opts.timeout) args.push('--timeout', String(opts.timeout)); if (opts.schema) args.push('--schema', opts.schema); if (opts.resume) args.push('--resume', opts.resume); const child = spawn('node', [WC3_CODE, ...args], { stdio: ['ignore', 'ignore', 'pipe'] }); let stderr = ''; child.stderr.on('data', (d) => { stderr += d; }); child.on('close', (code) => { if (code !== 0) return reject(new Error(`wc3-code exit ${code}: ${stderr.slice(0, 500)}`)); try { resolve(JSON.parse(readFileSync(outputFile, 'utf-8'))); } catch { resolve(readFileSync(outputFile, 'utf-8')); } }); chi ...[truncated 3149 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove cookie-value collection entirely. Replace it with a boolean computed inside the page: ```js hasLoginCookie: /(?:^|;\s*)(?:pin|unick|pt_pin)=/.test(document.cookie) ``` 2. Do not include `document.body.innerText` in the probe. Use narrow, non-sensitive status fields such as: - Current hostname and pathname, excluding query parameters. - Presence of known login, error, and review-container elements. - HTTP or page-state classifications computed locally. 3. Pass a separately constructed, allowlisted classification object to the LLM rather than serializing the original probe. 4. Keep page-state classification local where possible. Known URL and DOM patterns are sufficient for the documented states. 5. If an LLM is strictly required, require explicit user consent and document exactly which fields are transferred and where. 6. Restrict the endpoint: - Remove arbitrary environment-based redirection, or - Parse the destination with `new URL()`. - Reject credentials, non-HTTP(S) schemes, and destinations outside an explicit allowlist. - If the service is intended to be local, accept only loopback addresses such as `127.0.0.1` or `::1`. 7. Add automated tests asserting that prompts never contain cookie values, raw cookies, arbitrary body text, or URL query parameters. 8. Clear temporary prompt and response files after classification and create them with restrictive permissions. ]]>
