T09 · Insecure Skill Coding Practices
- Location
scripts/configure-keyapi-auth.mjs:119- Finding
KeyAPI Bearer Token Persisted in Plaintext Shell Profiles
- Content
View full analysis
Vulnerability Details
File Location:
scripts/configure-keyapi-auth.mjs:119-135, 192-222
Vulnerability Type: Plaintext credential storage and unsafe credential input
Risk Level: MediumVulnerable Code
js function buildManagedBlock(values, profileKind) { if (profileKind === "powershell") { return [ managedBlockStart, `$env:KEYAPI_TOKEN = ${powershellQuote(values.token)}`, managedBlockEnd, "" ].join("\n"); } return [ managedBlockStart, `export KEYAPI_TOKEN=${shellQuote(values.token)}`, managedBlockEnd, "" ].join("\n"); }js async function collectCredentials(args) { const token = args.token ? requireRealValue("KEYAPI_TOKEN", args.token) : undefined; if (token) { return { token }; } const rl = createInterface({ input, output }); try { output.write("KeyAPI setup will save KEYAPI_TOKEN into your shell environment variables.\n"); const enteredToken = await rl.question("Paste KEYAPI_TOKEN: "); return { token: requireRealValue("KEYAPI_TOKEN", enteredToken) }; } finally { rl.close(); } } function writeProfile(profilePath, credentials) { ensureParentDir(profilePath); const profileKind = detectProfileKind(profilePath); const existing = fs.existsSync(profilePath) ? fs.readFileSync(profilePath, "utf8") : ""; const cleaned = stripManagedBlock(existing); const block = buildManagedBlock(credentials, profileKind); const next = cleaned ? `${cleaned}\n\n${block}` : block; fs.writeFileSync(profilePath, next); return profileKind; }The associated documentation also recommends passing the token through a command-line argument:
bash node scripts/configure-keyapi-auth.mjs --token "your_keyapi_token"Technical Analysis
The setup helper permanently writes the KeyAPI bearer token into a standard shell startup ...[truncated 1954 chars]
- Remediation
View remediation
Remediation Suggestions
- Store the token in an operating-system credential manager, such as Windows Credential Manager, macOS Keychain, or a Linux Secret Service implementation.
- If a file-based fallback is required, use a dedicated credentials file rather than a shell startup file. Create it with owner-only permissions and verify the resulting mode before writing the token.
- Replace
readline.questionwith a hidden-input implementation that disables terminal echo while the token is entered. - Remove or deprecate
--token. Accept non-interactive credentials through protected standard input or a documented secret-manager integration. - If command-line token input remains supported, display a clear warning about process-list and shell-history exposure.
- Add an explicit removal command that deletes the managed credential block without affecting unrelated profile content.
- Document token rotation procedures and advise users to rotate any token entered into chat, exposed in history, or committed to a repository.
- Preserve the existing rule that tokens must never be printed or included in API response output.
