T09 · Insecure Skill Coding Practices
- Location
bin/run.mjs:205- Finding
API Credential Exposed Through Process Arguments and URL Query Parameters
- Content
View full analysis
Vulnerability Details
File Location:
bin/run.mjs:22-30, 110-123, 163-180, 205-215, 238-247;SKILL.md:46, 52
Vulnerability Type: API credential exposure through command-line arguments and URL query parameters
Risk Level: MediumVulnerable Code
The operation declares the authentication token as a query parameter:
js { "defaultValue": null, "description": "Authentication token for this API service.", "enumValues": [], "location": "query", "name": "token", "required": true, "schemaType": "string" },The token is accepted through a command-line argument:
js function parseArgs(argv) { const parsed = { operation: null, paramsJson: "{}", token: null }; for (let index = 0; index < argv.length; index += 1) { const flag = argv[index]; const value = argv[index + 1]; if (flag === "--operation") { parsed.operation = value; index += 1; continue; } if (flag === "--params-json") { parsed.paramsJson = value; index += 1; continue; } if (flag === "--token") { parsed.token = value; index += 1; continue; } fail(`Unknown argument "${flag}".`); } return parsed; }It is then inserted into the request parameters:
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; }All query parameters, including the token, are appended to the request URL:
js function applyQueryParams(operation, params, url) { for (const parameter of operation.parameters.filter((item) => item.location === "query")) { ...[truncated 3242 chars]- Remediation
View remediation
Remediation Suggestions
-
Read the credential directly from the environment inside the Node process instead of accepting it through
--token:js const token = process.env.JUST_ONE_API_TOKEN; if (!token) { fail("JUST_ONE_API_TOKEN is required."); }Update
SKILL.mdso the invocation does not contain a token argument:bash JUST_ONE_API_TOKEN="$JUST_ONE_API_TOKEN" \ node {baseDir}/bin/run.mjs \ --operation "searchProductsV1" \ --params-json '{"keyword":"<keyword>"}' -
If supported by JustOneAPI, transmit the credential in an authorization header instead of the query string:
js const requestInit = { headers: { accept: "application/json", authorization: `Bearer ${token}`, }, method: operation.method, }; -
Remove
tokenfrom the general parameter object so it cannot be serialized accidentally into URLs, response data, or diagnostic output. -
If the upstream API requires query-string authentication, document that residual risk and implement defensive controls:
- Never print or serialize the complete request URL.
- Redact
tokenfrom errors, traces, telemetry, and HTTP logs. - Configure proxies and API gateways not to retain sensitive query strings.
- Use narrowly scoped, short-lived tokens where available.
- Rotate the token after suspected exposure.
- Restrict access to process metadata and operational logs.
-
Add automated tests asserting that credentials never appear in standard output, standard error, diagnostic payloads, or process arguments.
-
