T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/m365-todo.mjs:94
- Finding
- OAuth Token Cache Is Written Without Explicit Restrictive Permissions## Vulnerability Details **File Location**: `scripts/m365-todo.mjs`, lines 94–97 **Vulnerability Type**: Insecure storage of OAuth credentials **Risk Level**: Medium ### Vulnerable Code ```js async function saveCache(pca, cachePath) { const serialized = await pca.getTokenCache().serialize(); fs.writeFileSync(cachePath, serialized, 'utf8'); } ``` ### Technical Analysis The serialized MSAL token cache is sensitive authentication material that may contain access tokens, refresh tokens, and account metadata. The Skill requests the `offline_access` scope, so the cache may permit authentication to be renewed beyond the lifetime of an individual access token. The cache is written using `fs.writeFileSync()` without an explicit restrictive file mode. For a newly created file, effective permissions depend on the host process's umask. If the configured cache file already exists with permissive permissions, this operation does not correct them. Consequently, the cache may be readable by other local users or processes on a shared or misconfigured host. The default location under the user's home directory reduces exposure on normally configured systems, but it does not guarantee secure permissions. The optional `M365_TOKEN_CACHE_PATH` also allows the cache to be placed in a less protected location. ### Attack Path 1. A victim authenticates through the Skill's Microsoft Entra device-code flow. 2. The Skill serializes the MSAL cache after authentication or silent token acquisition. 3. The cache is written to the default or configured path without enforcing mode `0600`. 4. A permissive umask, insecure pre-existing file, or unsafe custom path makes the cache readable by another local account or process. 5. A local attacker copies the serialized token cache. 6. The attacker imports or otherwise uses the cached credentials to obtain Microsoft Graph access as the victim. 7. The attacker reads or modifies resources authorized by the delegated scopes until the credentials ...[truncated 796 chars]
- Remediation
- ## Remediation Suggestions 1. Create the cache directory with owner-only permissions: ```js fs.mkdirSync(path.dirname(cachePath), { recursive: true, mode: 0o700, }); fs.chmodSync(path.dirname(cachePath), 0o700); ``` 2. Write the token cache with mode `0600`, and correct permissions on existing files: ```js fs.writeFileSync(cachePath, serialized, { encoding: 'utf8', mode: 0o600, }); fs.chmodSync(cachePath, 0o600); ``` 3. Use an atomic write strategy: create a temporary file in the same protected directory with `O_CREAT | O_EXCL`, mode `0600`, flush it, and rename it over the destination. 4. Before reading or replacing the cache, use `lstat()` and reject symbolic links and non-regular files. Where supported, use no-follow file-opening semantics to reduce symbolic-link attacks. 5. Validate custom `M365_TOKEN_CACHE_PATH` values and warn or fail if the parent directory or cache file is accessible to group or other users. 6. Prefer an operating-system credential manager or encrypted token-cache persistence rather than a plaintext filesystem cache. 7. Document the sensitivity of the cache, its expected permissions, credential revocation procedures, and the security implications of enabling `offline_access`.
