T09 · Insecure Skill Coding Practices
- Location
scripts/powpow-register.mjs:39- Finding
Predictable Shared Temporary State File Can Expose Session Credentials
- Content
View full analysis
Vulnerability Details
File Location:
scripts/powpow-register.mjs, lines 39–56 and 203–211
Vulnerability Type: Predictable insecure temporary file containing an authentication token
Risk Level: MediumVulnerable Code
js const BASE_URL = 'https://global.powpow.online'; const STATE_FILE = join(tmpdir(), 'powpow-register-state.json'); const PASSWORD_ENV = 'POWPOW_REGISTER_PASSWORD'; // ---------- State file ---------- function loadState() { try { return JSON.parse(readFileSync(STATE_FILE, 'utf8')); } catch { return {}; } } function saveState(patch) { const s = { ...loadState(), ...patch }; writeFileSync(STATE_FILE, JSON.stringify(s, null, 2), { mode: 0o600 }); return s; }The session token is subsequently written through this function:
js const token = r.cookies['powpow_token'] || ''; if (!token) die('Registration succeeded but no session cookie was captured'); saveState({ username: r.data?.user?.username || username, nickname: r.data?.user?.nickname || nickname, userId: r.data?.userId, token, step: 'registered-pending', registeredAt: new Date().toISOString(), });Technical Analysis
The state file uses a fixed, globally predictable name under the operating system's shared temporary directory:
text <system temporary directory>/powpow-register-state.jsonAlthough
writeFileSyncspecifies mode0600, that mode primarily controls permissions when a new file is created. It does not safely claim the path, verify ownership, reject an existing attacker-owned file, or enforce the requested permissions on a file that already exists.On a multi-user system where shared temporary-directory protections do not block this operation, a local attacker can pre-create the predictable path as an attacker-owned regular file with permissions that allow the Skill process to write it. When registration succeeds ...[truncated 2262 chars]
- Remediation
View remediation
Remediation Suggestions
- Create a private per-user or per-run directory with mode
0700, preferably under an application-specific user data directory rather than a shared temporary directory. - Use a cryptographically unpredictable directory name created with
mkdtempSync, and retain its path only for the current workflow. - Create the state file atomically with exclusive creation semantics such as
O_CREAT | O_EXCL, and reject the operation if the path already exists unexpectedly. - Use no-follow semantics where supported to prevent symbolic-link traversal.
- Before every read, write, or deletion, verify with
lstator an opened file descriptor that the object is a regular file, is owned by the current effective user, and is not linked unexpectedly. - Explicitly enforce mode
0600on the opened file rather than relying only on the creation mode. - Write updates to a securely created file in the same private directory and atomically rename it into place.
- Keep the existing cleanup operation, and additionally remove the private state directory after deleting its contents.
- Consider avoiding persistent storage of the JWT entirely when the workflow can keep it in process memory.
- Create a private per-user or per-run directory with mode
