T09 · Insecure Skill Coding Practices
Warning
- Location
- bin/run.mjs:216
- Finding
- API Credential Exposed Through Command-Line Arguments and URL Query Parameters## Vulnerability Details **File Location**: `bin/run.mjs:216-232`, `bin/run.mjs:350-370`, and `bin/run.mjs:382-390`; invocation documented at `SKILL.md:42` **Vulnerability Type**: API credential exposure through process metadata and URL query strings **Risk Level**: Medium ### Vulnerable Code At `bin/run.mjs:216-232`, the supplied token is injected into the operation parameters, after which all query parameters are added to the request URL: ```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, }; ``` At `bin/run.mjs:350-370`, the credential is assigned to `params.token`, while the manifest defines `token` as a query parameter: ```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; } function validateRequired(operation, params) { const missing = []; for (const parameter of operation.parameters) { if (parameter.required && params[parameter.name] === undefined) { missing.push(parameter.name); } } ``` At `bin/run.mjs:382-390`, every parameter whose manifest location is `query`, including `token`, is appended to the URL: ```js function applyQueryParams(operation, params, url) { for (const parameter of operation.parameters.filter((item) => item.locati ...[truncated 3436 chars]
- Remediation
- ## Remediation Suggestions 1. Read the token directly from `process.env.JUST_ONE_API_TOKEN` rather than requiring a `--token` command-line argument. 2. Send the credential in an authentication header supported by the service, preferably: ```js const token = process.env.JUST_ONE_API_TOKEN; if (!token) { fail("JUST_ONE_API_TOKEN is required."); } const requestInit = { method: operation.method, headers: { accept: "application/json", authorization: `Bearer ${token}`, }, }; ``` 3. Remove `token` from the operation query-parameter definitions and explicitly prevent callers from supplying `params.token`. 4. Update `SKILL.md` so documented commands do not include credentials in command-line arguments. 5. If JustOneAPI only supports query-string authentication, document that constraint and implement compensating controls: - Read the token only from the environment. - Disable or redact query-string logging in clients, gateways, proxies, monitoring systems, and server access logs. - Ensure errors never print the complete request URL. - Use narrowly scoped, short-lived tokens where supported. - Rotate tokens regularly and immediately after suspected exposure. - Restrict access to process telemetry and infrastructure logs. 6. Add automated tests confirming that secrets never appear in command arguments, standard output, standard error, exception details, or logged URLs.
