T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/gandi-api.js:66
- Finding
- Bearer Token Can Be Sent to an Arbitrary Configured HTTPS Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gandi-api.js:66-75, 216-242` **Vulnerability Type**: Unrestricted credential destination / credential exfiltration **Risk Level**: High ### Vulnerable Code ```js export function readApiUrl() { try { if (fs.existsSync(URL_FILE)) { const url = fs.readFileSync(URL_FILE, 'utf8').trim(); if (url) return url; } } catch (error) { // Ignore errors, use default } return DEFAULT_API_URL; } ``` ```js export function gandiApi(endpoint, method = 'GET', data = null, queryParams = {}, tokenOverride = null) { return new Promise((resolve, reject) => { const token = tokenOverride || readToken(); const apiUrl = readApiUrl(); // Build URL with query parameters const url = new URL(endpoint, apiUrl); Object.entries(queryParams).forEach(([key, value]) => { if (value !== undefined && value !== null) { url.searchParams.append(key, value); } }); const options = { method, headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/json' } }; // Add Content-Type for requests with body if (data && ['POST', 'PUT', 'PATCH'].includes(method)) { options.headers['Content-Type'] = 'application/json'; } const req = https.request(url, options, (res) => { ``` ### Technical Analysis The API base URL is loaded from `~/.config/gandi/api_url` and used without validating its hostname, port, or expected Gandi origin. The request code subsequently attaches the Gandi Personal Access Token to every request through the `Authorization` header. Use of HTTPS protects the connection in transit but does not establish that the recipient is Gandi. An attacker-controlled server with a valid TLS certificate can receive the token if the configuration file is modified to reference that server. A custom endpoint can be useful for sandbox testing, but unrestricted destinations exceed the minimum s ...[truncated 1350 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Allow only the documented Gandi origins by default: - `https://api.gandi.net` - `https://api.sandbox.gandi.net` 2. Parse the configured value with `new URL()` and reject: - Non-HTTPS protocols - Embedded usernames or passwords - Unexpected hostnames - Fragments - Unexpected ports 3. Associate credentials with their intended environment. A production token must never be sent to the sandbox or a custom endpoint, and vice versa. 4. If custom endpoints are required for development, require an explicit unsafe-development flag and separate test credentials. 5. Log the selected hostname before authentication without logging the token. 6. Apply restrictive permissions to `api_url` and its parent directory, although permissions should supplement rather than replace destination validation. 7. Consider pinning requests to a fixed API origin in production instead of accepting a file-based override. ]]>
