T09 · Insecure Skill Coding Practices
- Location
api/browser/open-url.js:18- Finding
Operating System Command Injection in URL-Opening Helper
- Content
View full analysis
hostname === host || hostname.endsWith('.' + host)) } catch { return false } } async function openUrl(url) { if (!url) throw new Error('URL is required') if (!isAllowedUrl(url)) throw new Error(`URL not allowed: ${url}`) if (process.platform === 'darwin') { await execAsync(`open "${url}"`) } else if (process.platform === 'win32') { await execAsync(`start "" "${url}"`) } else { await execAsync(`xdg-open "${url}"`) } } ``` The file also exposes a command-line interface accepting the URL directly: ```javascript if (require.main === module) { openUrl(process.argv[2]) .then(() => { console.log(`✅ Opened: ${process.argv[2]}`); process.exit(0) }) .catch(err => { console.error(`❌ ${err.message}`); process.exit(1) }) } ``` ### Technical Analysis The helper validates the URL by parsing it with `new URL()`, but subsequently passes the original, unmodified URL into a shell command through `child_process.exec`. Enclosing the URL in double quotes does not make the command safe. On POSIX shells, command substitution constructs such as `$()` and backticks are evaluated inside double-quoted strings. Therefore, a URL can have an allowed HTTPS hostname while containing shell syntax in its path. The hostname allowlist only mitigates requests to unauthorized hosts; it does not sanitize the value for use as a shell argument. The helper is exported and is also directly executable from the command line, creating a concrete attacker-controlled input path. ### Attack Path 1. An attacker supplies a URL whose hostname is `i.beautsgo.com`, satisfying `isAll ...[truncated 1515 chars]- Remediation
View remediation
