T09 · Insecure Skill Coding Practices
- Location
scripts/cdp-utils.js:23- Finding
Shell Command Injection Through Unsafe CDP Command Construction
- Content
View full analysis
`"${a.replace(/"/g, '\\"')}"`).join(" "); try { return execSync(`agent-browser --cdp ${port} ${cmd}`, { encoding: "utf-8", timeout: 20000, stdio: ["pipe", "pipe", "pipe"], }).trim(); } catch (e) { return e.stdout?.trim() || ""; } } ``` Relevant page-derived input in `scripts/fanqie-rank-scraper.js`: ```js function buildCategoriesJS(prefix) { return `JSON.stringify((function(){ var prefix=${JSON.stringify(prefix)}; var out=[];var seen={}; Array.from(document.querySelectorAll('a')).forEach(function(a){ var href=a.getAttribute('href')||''; if(href.indexOf(prefix)===-1)return; var name=(a.innerText||a.textContent||'').trim(); if(!name)return; if(seen[href])return;seen[href]=1; out.push({name:name,href:href}); }); return out; })())`; } ``` ```js ab(PORT, "open", `https://fanqienovel.com${cat.href}`); ``` Relevant page-derived input in `scripts/qidian-rank-scraper.js`: ```js var href=a.getAttribute('href')||a.href||''; var url=href?(href.indexOf('http')===0?href:'https:'+href):''; ``` ```js ab(port, "open", b.url); ``` ### Technical Analysis The shared `ab()` helper creates one command string and passes it to `execSync()`, causing Node.js to invoke a system shell. Each argument is surrounded by double quotes, but only literal double-quote characters are escaped. Double quoting does not suppress all shell evaluation. In common shells, command substitutions such as `$(command)` and backtick expressions are still evaluated inside double-quoted strings. Consequently, an argument containing shell substitution syntax ca ...[truncated 2587 chars]- Remediation
View remediation
65535) { throw new Error("Invalid CDP port"); } try { return execFileSync( "agent-browser", ["--cdp", String(parsedPort), ...args.map(String)], { encoding: "utf-8", timeout: 20000, stdio: ["pipe", "pipe", "pipe"], } ).trim(); } catch (e) { return typeof e.stdout === "string" ? e.stdout.trim() : ""; } } ``` This passes arguments directly to the executable without asking a shell to parse them. 2. **Validate every navigation URL** Before calling `ab(port, "open", value)`, parse the value with `new URL()` and enforce: - The protocol must be `https:`. - The hostname must exactly match an approved platform host. - Username and password components must be empty. - Unexpected ports must be rejected. - Relative links must be resolved against a fixed trusted origin. Example: ```js function trustedUrl(value, base, allowedHosts) { const url = new URL(value, base); if (url.protocol !== "https:") { throw new Error("Non-HTTPS URL rejected"); } if (!allowedHosts.includes(url.hostname)) { throw new Error(`Untrusted hostname: ${url.hostname}`); } if (url.username || url.password || url.port) { throw new Error("URL credentials or custom ports are not allowed"); } return url.toString(); } ``` 3. **Apply platform-specific hostname allowlists** Use narrow allowlists such as: - Fanqie: `fanqienovel.com` - Qidian PC: `www.qidian.com` - Qidian mobile: `m.qidian.com` - Qimao: `www.qimao.com` - JJWXC: `www.jjwxc.net` - Ciweimao: `www.ciweimao.com` Include additional subdomains only when they are verified as necessary. 4. * ...[truncated 658 chars]
