T09 · Insecure Skill Coding Practices
- Location
- clawflight.js:46
- Finding
- OAuth Bearer Token Stored in a Plaintext File Without Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `clawflight.js`, lines 46–76 **Vulnerability Type**: Plaintext sensitive-token storage with insufficient filesystem protection **Risk Level**: Medium ### Vulnerable Code ```js async function getAmadeusToken() { // Check cache first if (existsSync(TOKEN_CACHE_FILE)) { try { const cached = JSON.parse(readFileSync(TOKEN_CACHE_FILE, 'utf-8')); if (cached.expires_at > Date.now() + 60000) { return cached.access_token; } } catch (e) { /* ignore */ } } // Fetch new token const response = await axios.post( `${AMADEUS_BASE_URL}/v1/security/oauth2/token`, new URLSearchParams({ grant_type: 'client_credentials', client_id: AMADEUS_CLIENT_ID, client_secret: AMADEUS_CLIENT_SECRET, }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ); const token = { access_token: response.data.access_token, expires_at: Date.now() + (response.data.expires_in * 1000), }; writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(token)); return token.access_token; } ``` The cache path is defined at line 27: ```js const TOKEN_CACHE_FILE = join(PROJECT_ROOT, 'data', '.amadeus-token.json'); ``` ### Technical Analysis After obtaining an OAuth access token from the fixed, official Amadeus HTTPS endpoint, the application stores the bearer token as plaintext JSON in the project-level `data` directory. The call to `writeFileSync` does not specify a restrictive file mode. For a newly created file, effective permissions therefore depend on the process umask and surrounding directory permissions. In a shared or incorrectly configured environment, other local accounts or processes may be able to read the token. A bearer token grants access based solely on possession. Any process that retrieves the cached value can replay it against the Amadeus API until it expires. Although caching reduces authentication requests, persistent token storag ...[truncated 2204 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Avoid persistent token caching when unnecessary.** Retain the access token only in process memory if the command lifecycle and API usage permit it. 2. **Use a private credential or cache location.** If persistence is required, use an operating-system credential store or a per-user cache directory instead of the project data directory. 3. **Enforce restrictive permissions.** Create the parent directory with mode `0700` and the cache file with mode `0600`, for example: ```js import { mkdirSync, writeFileSync } from 'fs'; mkdirSync(PRIVATE_CACHE_DIR, { recursive: true, mode: 0o700, }); writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(token), { encoding: 'utf8', mode: 0o600, flag: 'w', }); ``` 4. **Harden replacement behavior.** Write to a securely created temporary file in the same private directory and atomically rename it into place. Reject symbolic links and verify that existing cache files are regular files owned by the current user before reading or replacing them. 5. **Correct existing permissions.** Do not assume that supplying `mode` will repair an already existing permissive file. Explicitly validate and, where appropriate, change existing file permissions to `0600`. 6. **Limit token exposure.** Never print the token in errors or logs, delete expired cache entries, and keep access-token lifetimes and API scopes as narrow as Amadeus supports. 7. **Protect repository and backup boundaries.** Add the cache file to ignore rules, document that it contains sensitive authentication material, and exclude it from source-control commits and broadly accessible backups. ]]>
