T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/qwen_chat.js:280
- Finding
- Weak URL Validation Can Select an Attacker-Controlled Browser Tab## Vulnerability Details **File Location**: `scripts/qwen_chat.js:280-286` **Related Location**: `scripts/qwen_chat.js:400-405` **Vulnerability Type**: Improper origin validation **Risk Level**: Medium ### Vulnerable Code ```js async function getQwenTab(token) { const res = await httpGet(`http://127.0.0.1:${RELAY_PORT}/json`, { 'x-openclaw-relay-token': token }); if (res.status !== 200) throw new Error(`Relay /json failed: ${res.status}`); const tabs = JSON.parse(res.body); const qwen = tabs.find(t => t.url.includes('chat.qwen.ai')); if (!qwen) throw new Error('No Qwen Chat tab found. Open chat.qwen.ai in Chrome and attach extension.'); return qwen; } ``` The same unsafe matching pattern is also used by the status command: ```js const tabs = await httpGet(`http://127.0.0.1:${RELAY_PORT}/json`, { 'x-openclaw-relay-token': token }); const all = JSON.parse(tabs.body); const qwen = all.find(t => t.url.includes('chat.qwen.ai')); ``` ### Technical Analysis The script identifies a trusted Qwen tab using a substring search: ```js t.url.includes('chat.qwen.ai') ``` This does not validate the parsed URL's scheme or hostname. Consequently, it accepts unrelated URLs that merely contain the trusted domain as text, including: ```text https://attacker.example/?next=chat.qwen.ai https://chat.qwen.ai.attacker.example/ ``` After selection, the script attaches to the tab through the Chrome DevTools Protocol, executes JavaScript in its context, injects the user's prompt through input events, and extracts content from its DOM. Because `Array.find()` selects the first matching attached tab, an attacker-controlled tab appearing before the legitimate Qwen tab can be treated as the trusted destination. Relay authentication protects access to the local relay but does not establish that the selected browser page belongs to the expected HTTPS origin. ### Attack Path 1. An attacker causes a crafted page whose URL contains the string `chat.qwen.ai` to be o ...[truncated 1645 chars]
- Remediation
- ## Remediation Suggestions Parse each candidate URL and require an exact trusted HTTPS origin rather than a substring match: ```js function isTrustedQwenUrl(value) { try { const url = new URL(value); return url.protocol === 'https:' && url.hostname === 'chat.qwen.ai' && url.port === ''; } catch { return false; } } ``` Apply the predicate consistently in both `getQwenTab()` and `status()`: ```js const qwenTabs = tabs.filter(tab => isTrustedQwenUrl(tab.url)); if (qwenTabs.length === 0) { throw new Error( 'No trusted Qwen Chat tab found. Open https://chat.qwen.ai and attach the extension.' ); } if (qwenTabs.length > 1) { throw new Error( 'Multiple trusted Qwen tabs are attached; select an explicit target before continuing.' ); } return qwenTabs[0]; ``` Additional hardening should include: 1. Revalidate the target's `location.origin` after CDP attachment and before injecting input: ```js const origin = await evalCmd('location.origin'); if (origin !== 'https://chat.qwen.ai') { throw new Error('Attached target has an unexpected origin'); } ``` 2. Require an explicit target ID when multiple legitimate Qwen tabs are attached. 3. Reject malformed URLs and non-HTTPS schemes. 4. Repeat origin validation before sensitive operations to reduce target-navigation race risks. 5. Add tests covering deceptive URLs, including subdomains, query strings, fragments, user-information components, and malformed values.
