T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/praxis-gws.js:24
- Finding
- OAuth credentials and tokens are stored without explicit restrictive filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/praxis-gws.js:24-29`, `scripts/praxis-gws.js:76-83`, and `scripts/praxis-gws.js:261-269` **Vulnerability Type**: Insecure storage of sensitive OAuth material **Risk Level**: Medium ### Vulnerable Code ```js const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.config', 'praxis-gws'); const TOKEN_PATH = path.join(CONFIG_DIR, 'token.json'); const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json'); // Ensure config directory exists fs.mkdirSync(CONFIG_DIR, { recursive: true }); ``` ```js oAuth2Client.getToken(code, (err, token) => { if (err) { console.error('Error retrieving access token', err); process.exit(1); } oAuth2Client.setCredentials(token); fs.writeFileSync(TOKEN_PATH, JSON.stringify(token)); console.log('Token stored to', TOKEN_PATH); resolve(oAuth2Client); }); ``` ```js credentials(srcPath) { if (!fs.existsSync(srcPath)) { console.error('Error: File not found:', srcPath); process.exit(1); } fs.copyFileSync(srcPath, CREDENTIALS_PATH); console.log('Credentials saved to', CREDENTIALS_PATH); console.log('Run any command to start OAuth flow'); }, ``` ### Technical Analysis The configuration directory, OAuth client credentials, and OAuth token are created or copied without explicitly enforcing owner-only permissions. Their effective permissions consequently depend on the process umask and, for copied credentials, filesystem behavior and source-file metadata. `token.json` may contain a long-lived refresh token in addition to a temporary access token. The credentials file contains the OAuth client identifier and client secret. On a multi-user host, container, shared workspace, or environment with a permissive umask, another local account or compromised process may be able to read these files. The implementation also does not validate that the sensitive paths are regular files owned by the current user or reject symbolic links before readi ...[truncated 1573 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the configuration directory with owner-only permissions: ```js fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); fs.chmodSync(CONFIG_DIR, 0o700); ``` 2. Write tokens atomically with mode `0600`. Create a temporary file in the same protected directory, flush it, and rename it into place: ```js const temporaryPath = `${TOKEN_PATH}.tmp-${process.pid}`; fs.writeFileSync(temporaryPath, JSON.stringify(token), { encoding: 'utf8', mode: 0o600, flag: 'wx', }); fs.renameSync(temporaryPath, TOKEN_PATH); fs.chmodSync(TOKEN_PATH, 0o600); ``` 3. After copying OAuth credentials, explicitly restrict their permissions: ```js fs.copyFileSync(srcPath, CREDENTIALS_PATH); fs.chmodSync(CREDENTIALS_PATH, 0o600); ``` 4. Before reading or replacing sensitive files, use `lstatSync` to reject symbolic links and confirm that each path is a regular file owned by the current user. 5. Fail securely if permissions are broader than intended, particularly on shared systems. 6. Document token revocation procedures and advise affected users to revoke existing OAuth grants if token exposure is suspected. ]]>
