T09 · Insecure Skill Coding Practices
- Location
scripts/cozi.js:112- Finding
Bearer Session Token Stored Without Restrictive File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cozi.js, lines 112–114
Vulnerability Type: Plaintext sensitive token storage with permissions dependent on the process umask
Risk Level: MediumVulnerable Code
js function saveSession(session) { fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2)); }The stored session object is constructed at lines 130–135 and includes sensitive authentication and account data:
js const session = { accessToken: response.accessToken, accountId: response.accountId, accountPersonId: response.accountPersonId, expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString() };Technical Analysis
The Skill caches its Cozi bearer token in
.session.jsonunder the Skill directory. The call tofs.writeFileSyncdoes not specify a restrictive file mode, so the permissions assigned when the file is created depend on the runtime environment's umask.In an environment with a permissive umask or insufficiently protected Skill directory, another local user or process may be able to read the cached bearer token. Because bearer-token possession is sufficient for API authentication, no knowledge of the original Cozi password would be required to use a stolen, unexpired token.
This access is related to the declared session-caching feature, but secure plaintext secret handling requires explicit access restrictions rather than reliance on ambient process configuration.
Attack Path
- A victim runs any Cozi command while no valid cached session exists.
- The Skill authenticates to
rest.cozi.comusing the configured Cozi credentials. - The authentication response is written to
.session.json, including the bearer token and account identifiers. - The file is created using permissions derived from the process umask rather than an explicit owner-only mode.
- A local attacker or untrusted local process with read access obtains the bearer token.
- Before the token exp ...[truncated 873 chars]
- Remediation
View remediation
Remediation Suggestions
Create and maintain the session file with owner-only permissions:
js function saveSession(session) { fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2), { encoding: 'utf8', mode: 0o600 }); fs.chmodSync(SESSION_FILE, 0o600); }Additional hardening should include:
- Ensure the parent Skill directory is accessible only to the owning user, such as mode
0700. - Validate and repair permissions on an existing session file before reading it.
- Reject a session file that is not a regular file or is owned by another user, where the platform exposes ownership information.
- Consider storing the bearer token in an operating-system credential manager instead of a plaintext JSON file.
- Delete the cached token when it expires and provide an explicit logout or session-clearing command.
- Document that the session cache contains an authentication secret and must not be shared, backed up insecurely, or committed to source control.
- Ensure the parent Skill directory is accessible only to the owning user, such as mode
