T09 · Insecure Skill Coding Practices
Warning
- Location
- bin/run.mjs:226
- Finding
- API Token Exposure Through Process Arguments and URL Query Parameters## Vulnerability Details **File Location**: `bin/run.mjs:117-139, 226-236, 269-278`; usage documented at `SKILL.md:48` **Vulnerability Type**: API credential exposure through command-line arguments and query-string authentication **Risk Level**: Medium ### Vulnerable Code `SKILL.md:48`: ```bash node {baseDir}/bin/run.mjs --operation "searchItemListV1" --token "$JUST_ONE_API_TOKEN" --params-json '{"keyword":"<keyword>"}' ``` `bin/run.mjs:117-139`: ```js 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); ``` `bin/run.mjs:226-236`: ```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; } ``` `bin/run.mjs:269-278`: ```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); } } ``` ### Technica ...[truncated 2962 chars]
- Remediation
- ## Remediation Suggestions 1. **Stop passing secrets through command-line arguments.** Read the credential directly from `process.env.JUST_ONE_API_TOKEN` and remove or deprecate `--token`. ```js function injectToken(operation, params) { const tokenParam = operation.parameters.find( (parameter) => parameter.name === "token" ); if (!tokenParam || params.token !== undefined) { return; } const token = process.env.JUST_ONE_API_TOKEN; if (!token) { fail("JUST_ONE_API_TOKEN is required.", { operationId: operation.operationId, }); } params.token = token; } ``` 2. **Prefer header-based authentication.** If supported by JustOneAPI, remove `token` from query parameters and send it in the provider's documented authentication header, such as: ```js requestInit.headers.authorization = `Bearer ${token}`; ``` The exact header and scheme must match the provider's official authentication specification. 3. **Prevent token injection through `--params-json`.** Reject sensitive authentication fields supplied as ordinary parameters and source credentials only from the dedicated protected mechanism. ```js if (Object.prototype.hasOwnProperty.call(params, "token")) { fail('Do not provide "token" through --params-json.'); } ``` 4. **If query authentication is mandatory, minimize secondary exposure.** Never print or log the complete request URL, configure proxies and observability systems to redact the `token` query parameter, disable query-string capture where feasible, and apply short token lifetimes and narrowly scoped permissions. 5. **Update `SKILL.md`.** Replace the `--token` example with an environment-only invocation and explicitly state that credentials must not be included in `--params-json`. 6. **Rotate potentially exposed credentials.** Revoke and replace tokens previously used through c ...[truncated 109 chars]
