T09 · Insecure Skill Coding Practices
Error
- Location
- cli.mjs:55
- Finding
- Basic Authentication Credentials May Be Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `cli.mjs:55-58`, with credential transmission at `cli.mjs:199-206` and `cli.mjs:215-221` **Vulnerability Type**: Insecure transport of reusable credentials **Risk Level**: High ### Vulnerable Code ```js function buildBaseUrl(domain) { if (!domain) return ''; if (domain.startsWith('http://') || domain.startsWith('https://')) return domain.replace(/\/$/, ''); return `https://${domain.replace(/\/$/, '')}`; } ``` ```js async function requestJson(baseUrl, username, password, pathAndQuery) { const auth = Buffer.from(`${username}:${password}`).toString('base64'); const url = `${baseUrl}${pathAndQuery}`; const response = await fetch(url, { headers: { 'Accept': 'application/json', 'Authorization': `Basic ${auth}`, }, }); const text = await response.text(); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${text.slice(0, 500)}`); } return text; } async function requestBinary(baseUrl, username, password, pathAndQuery) { const auth = Buffer.from(`${username}:${password}`).toString('base64'); const url = `${baseUrl}${pathAndQuery}`; const response = await fetch(url, { headers: { 'Authorization': `Basic ${auth}`, }, }); ``` ### Technical Analysis The URL builder explicitly accepts both HTTPS and plaintext HTTP URLs. The request functions then place the configured ServiceNow username and password in an HTTP Basic Authentication header for every request. Basic Authentication only Base64-encodes the credentials; it does not encrypt them. If the configured domain uses `http://`, any party able to observe or modify the network connection can recover the original username and password. This is unnecessary for the declared functionality because ServiceNow API authentication can and should be restricted to HTTPS. The destination is user-configurable. No hidden third-party exfiltration endpoint was found, but the lack of transport enforcement makes the ...[truncated 1235 chars]
- Remediation
- ## Remediation Suggestions - Parse the configured base URL with the standard `URL` class and reject every protocol other than `https:`. - Reject URLs containing embedded usernames or passwords. - Consider restricting destination hostnames to an organization-managed allowlist or an approved ServiceNow domain suffix. - Do not silently downgrade or redirect authenticated requests to HTTP. Explicitly verify redirect behavior for credential-bearing requests. - Prefer scoped, short-lived OAuth tokens over reusable account passwords. - Require a dedicated ServiceNow account with server-enforced read-only roles; do not rely solely on the CLI’s use of GET. - Add automated tests proving that `http://` URLs, malformed URLs, and redirects to non-HTTPS destinations are rejected before an Authorization header is sent. A hardened URL builder should follow this pattern: ```js function buildBaseUrl(domain) { if (!domain) return ''; const candidate = /^[a-z][a-z0-9+.-]*:\/\//i.test(domain) ? domain : `https://${domain}`; const url = new URL(candidate); if (url.protocol !== 'https:') { throw new Error('Only HTTPS ServiceNow endpoints are allowed'); } if (url.username || url.password) { throw new Error('Credentials must not be embedded in the ServiceNow URL'); } return url.origin; } ```
