T09 · Insecure Skill Coding Practices
- Location
references/selectors-and-handoffs.md:48- Finding
Predictable Plaintext Temporary File Used for One-Time Secrets
- Content
View full analysis
Vulnerability Details
File Location:
references/selectors-and-handoffs.md, lines 48-59
Vulnerability Type: Predictable and insecure temporary-file handling of sensitive credentials
Risk Level: MediumVulnerable Code:
markdown ## One-time secrets Some values (API keys, tokens) are shown **once** in the page. Handle them without leaking: - Pull the value out of the page text straight into a file — never echo it to chat or logs: ```js const text = await page.evaluate(() => document.body.innerText) const m = text.match(/<expected-pattern>/) require('fs').writeFileSync('secret.txt', m[0]) ``` - Move it into the user's password manager (whatever they use), then **shred the temp file**: ```bash # store via the user's password-manager CLI, then: shred -u secret.txt # or: rm -P secret.txt ```Technical Analysis
The documented workflow extracts an API key or token from the authenticated browser and writes it to the fixed relative path
secret.txt. This is unsafe for sensitive temporary data because:- The filename and location are predictable.
writeFileSyncfollows an existing symbolic link and does not use exclusive creation.- The resulting permissions depend on the process umask and may be broader than intended.
- The secret remains as plaintext on disk until it is transferred and deleted.
shredandrm -Pdo not reliably erase data on copy-on-write, journaled, networked, snapshotted, or SSD-backed filesystems.- Cleanup is not placed in a guaranteed error-handling path, so failures may leave the secret behind.
A local process with access to the working directory could pre-create
secret.txt, replace it with a symbolic link, monitor its creation, or read it before cleanup. A symbolic-link attack could also redirect the secret into another file writable by the Agent process.Attack Path
- An attacker or untrusted local proces ...[truncated 1477 chars]
- Remediation
View remediation
Remediation Suggestions
Prefer avoiding intermediate files entirely:
- Pass the secret directly to the user's password-manager CLI or API through standard input.
- Ensure the receiving command does not expose the secret in command-line arguments, process listings, stdout, stderr, or logs.
- Clear in-memory references as soon as practical and avoid returning the value through chat or tool output.
If a temporary file is unavoidable:
- Create a randomized private directory using
fs.mkdtemp. - Set the directory permissions to
0700. - Create the file atomically with exclusive mode (
wx) and permissions0600. - Reject existing paths and symbolic links rather than following them.
- Keep the file open only as long as necessary and transfer it immediately to the password manager.
- Place cleanup in a
finallyblock so it executes after both success and failure. - Delete the entire private temporary directory afterward.
- Treat deletion as lifecycle cleanup rather than guaranteed forensic erasure; account for snapshots, backups, journaling, and copy-on-write storage.
Example hardened creation pattern:
js import fs from 'node:fs' import os from 'node:os' import path from 'node:path' const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'browser-driver-')) fs.chmodSync(dir, 0o700) const secretPath = path.join(dir, 'secret') try { fs.writeFileSync(secretPath, secret, { flag: 'wx', mode: 0o600 }) // Transfer through a password-manager interface that does not log the value. } finally { fs.rmSync(dir, { recursive: true, force: true }) }
