T09 · Insecure Skill Coding Practices
Warning
- Location
- bin/run.mjs:346
- Finding
- API Credential Exposed Through URL Query String and Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `bin/run.mjs:346-368`, `bin/run.mjs:455-465`, and `bin/run.mjs:498-519` **Vulnerability Type**: Sensitive credential exposure **Risk Level**: Medium ### Vulnerable Code ```js const params = parseParams(args.paramsJson); applyDefaults(operation, params); injectToken(operation, params, args.token); validateRequired(operation, params); const baseUrl = manifest.baseUrl; const url = new URL(operation.path, ensureBaseUrl(baseUrl)); applyPathParams(operation, params, url); applyQueryParams(operation, params, url); const requestInit = { headers: { "accept": "application/json", }, method: operation.method, }; if (operation.requestBody && params.body !== undefined) { requestInit.body = JSON.stringify(params.body); requestInit.headers["content-type"] = operation.requestBody.contentType || "application/json"; } let response; try { response = await fetch(url, requestInit); ``` ```js function injectToken(operation, params, cliToken) { const tokenParam = operation.parameters.find((parameter) => parameter.name === "token"); if (!tokenParam || params.token !== undefined) { return; } if (!cliToken) { fail("--token is required for this operation.", { operationId: operation.operationId, }); } params.token = cliToken; } ``` ```js function applyQueryParams(operation, params, url) { for (const parameter of operation.parameters.filter((item) => item.location === "query")) { const value = params[parameter.name]; if (value === undefined) { continue; } appendValue(url.searchParams, parameter.name, value); } } function appendValue(searchParams, name, value) { if (Array.isArray(value)) { for (const item of value) { appendValue(searchParams, name, item); } return; } if (value && typeof value === "object") { searchParams.append(name, JSON.stringify(value)); return; } searchParams.append(name, String(value)); } ``` ### Tec ...[truncated 2308 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Move authentication out of the URL.** Prefer an `Authorization` header or another dedicated authentication header supported by JustOneAPI, for example: ```js const requestInit = { method: operation.method, headers: { accept: "application/json", authorization: `Bearer ${token}`, }, }; ``` Do not add the token to `params` or `URLSearchParams` when header-based authentication is available. 2. **Read the credential directly from the environment.** Use `process.env.JUST_ONE_API_TOKEN` instead of requiring `--token`, thereby reducing exposure through process listings and shell history. If a CLI option must remain for compatibility, prefer the environment variable and clearly warn about the CLI exposure. 3. **Prevent user-supplied token duplication.** Reject or remove `token` from `--params-json` so callers cannot accidentally bypass safer credential handling and place a token back into the query string. 4. **Redact credentials from observability systems.** Configure application logs, reverse proxies, API gateways, monitoring tools, and error-reporting systems to remove the `token` query parameter. Never log complete authenticated URLs. 5. **Apply token-side controls.** Use narrowly scoped credentials where supported, enforce rate and spending limits, rotate exposed tokens, and monitor for anomalous usage. 6. **If the upstream API mandates query authentication**, document the residual risk, avoid command-line credential input, enforce URL-query redaction throughout the request path, and recommend regular token rotation. ]]>
