T09 · Insecure Skill Coding Practices
Error
- Location
- session-token.js:14
- Finding
- Plaintext Session Token Storage and Disclosure Through CLI Arguments and Output<![CDATA[ ## Vulnerability Details **File Location**: `session-token.js`, lines 14-42 **Vulnerability Type**: Plaintext sensitive-data storage and credential disclosure **Risk Level**: High ### Vulnerable Code ```javascript const TOKEN_FILE = path.join(__dirname, ".session_token") function saveToken(token) { fs.writeFileSync(TOKEN_FILE, token, "utf-8") process.stdout.write(JSON.stringify({ saved: true, sessionToken: token })) } function loadToken() { if (fs.existsSync(TOKEN_FILE)) { const token = fs.readFileSync(TOKEN_FILE, "utf-8").trim() if (token) { process.stdout.write(token) return } } process.stderr.write("No session token found. User is not logged in.\n") process.exit(1) } function checkLogin() { let loggedIn = false let token = null if (fs.existsSync(TOKEN_FILE)) { const stored = fs.readFileSync(TOKEN_FILE, "utf-8").trim() if (stored) { loggedIn = true token = stored } } process.stdout.write(JSON.stringify({ loggedIn, sessionToken: token })) } ``` The documented command interface also requires the token to be passed as a command-line argument: ```text node pay-bills-skill/session-token.js save <token> ``` ### Technical Analysis The bearer session token is stored unencrypted in `.session_token`. The call to `fs.writeFileSync()` does not specify a restrictive file mode, so the resulting permissions depend on the process umask and existing file permissions. On a shared or incorrectly configured system, another local user or process may be able to read the token. The token is additionally exposed through several channels: - The `save` command accepts it as a command-line argument, potentially exposing it through process inspection, command history, execution telemetry, or Agent transcripts. - The `save` command returns the complete token in its JSON output. - The `load` command prints the raw token. - The `check` command returns the raw token in its JSON output. A bearer token is sufficient to authenticate with ...[truncated 1625 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store session tokens in an operating-system credential manager or another protected secret-storage facility rather than in the project directory. 2. If file storage is unavoidable: - Create the token file with mode `0600`. - Verify that the file is owned by the expected user. - Refuse to use files with unsafe ownership, symbolic links, or excessive permissions. - Write atomically through a securely created temporary file in the same protected directory. 3. Do not pass tokens as command-line arguments. Read them from protected standard input or receive them directly through an in-process secret API. 4. Change `save` output to a non-sensitive status such as: ```json { "saved": true } ``` 5. Change `check` to return only whether a session exists: ```json { "loggedIn": true } ``` 6. Remove or tightly restrict functionality that prints the raw bearer token. 7. Ensure command output, error reporting, telemetry, and Agent transcripts redact authentication credentials. 8. Use short-lived tokens with server-side expiration, rotation, and immediate revocation on logout or suspected disclosure. ]]>
