T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/bookmark-poll.js:33
- Finding
- OAuth token cache may be created with overly permissive filesystem permissions## Vulnerability Details **File Location**: `scripts/bookmark-poll.js:33-38`; duplicate vulnerable write at `scripts/backlog-sweep.js:94-95` **Vulnerability Type**: Sensitive credential storage with permissions inherited from the process umask **Risk Level**: High ### Vulnerable Code `scripts/bookmark-poll.js:33-38`: ```js function saveTokenCache(data) { fs.mkdirSync(path.dirname(TOKEN_CACHE), { recursive: true }); fs.writeFileSync(TOKEN_CACHE, JSON.stringify({ ...data, cached_at: Date.now() }, null, 2)); } ``` `scripts/backlog-sweep.js:94-95`: ```js fs.mkdirSync(path.dirname(TOKEN_CACHE), { recursive: true }); fs.writeFileSync(TOKEN_CACHE, JSON.stringify({ ...data, cached_at: Date.now() }, null, 2)); ``` ### Technical Analysis Both scripts persist the complete OAuth token response to `data/x-oauth2-token-cache.json` without specifying a restrictive file mode. The response contains an X access token and may also contain a refresh token. When the cache file is first created, its permissions are determined by the process umask. Under a common `022` umask, the resulting file may be readable by other local users. This behavior contradicts the security claim in `README.md` that OAuth tokens are stored with mode `0o600`. Although `scripts/x-oauth2-authorize.js` and the rotated-token write explicitly use `0o600`, subsequent cache creation in these two polling scripts does not provide the same protection. If the file already exists with insecure permissions, adding a mode only to later writes is insufficient because opening an existing file does not necessarily replace its mode. Existing cache files must also be explicitly repaired. ### Attack Path 1. A user configures valid X OAuth credentials and runs `bookmark-poll.js` or `backlog-sweep.js`. 2. No token cache currently exists, so the script creates `data/x-oauth2-token-cache.json`. 3. The process uses a permissive umask, causing the file t ...[truncated 1112 chars]
- Remediation
- ## Remediation Suggestions 1. Create the data directory with owner-only permissions and explicitly set token files to `0o600`: ```js function saveTokenCache(data) { fs.mkdirSync(path.dirname(TOKEN_CACHE), { recursive: true, mode: 0o700 }); fs.writeFileSync( TOKEN_CACHE, JSON.stringify({ ...data, cached_at: Date.now() }, null, 2), { mode: 0o600 } ); fs.chmodSync(TOKEN_CACHE, 0o600); } ``` 2. Apply the same correction in `backlog-sweep.js`. 3. Repair existing installations during startup by checking and resetting the cache mode with `fs.chmodSync`. 4. Prefer atomic credential writes: write to a randomly named owner-only file in the same directory, call `fsync`, set mode `0o600`, and rename it over the destination. 5. Store refresh tokens in an operating-system credential store or dedicated secrets manager where practical. 6. Add a setup check that rejects or warns about token files readable by group or other users. 7. Update documentation so its `0o600` claim is verified by all token-writing paths.
