T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/deploy.mjs:124
- Finding
- Time-of-check to time-of-use race can upload unintended local files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/deploy.mjs`, lines 124–140 **Vulnerability Type**: Filesystem TOCTOU race and potential local-file disclosure **Risk Level**: Medium ### Vulnerable Code ```js function collectFiles(dirPath, basePath = "") { const files = []; const entries = readdirSync(dirPath); for (const entry of entries) { const fullPath = join(dirPath, entry); const relPath = join(basePath, entry); const stat = lstatSync(fullPath); if (stat.isSymbolicLink()) { throw new Error(`Symlinks are not allowed in the static directory: ${fullPath}`); } if (stat.isDirectory()) { files.push(...collectFiles(fullPath, relPath)); } else if (stat.isFile()) { const content = readFileSync(fullPath); ``` ### Technical Analysis The code validates each path using `lstatSync()` and subsequently accesses the same path using a separate `readFileSync()` operation or recursive directory traversal. These operations resolve the pathname independently. An attacker or concurrent process with write access to the selected static directory can replace a validated regular file or directory with a symbolic link after `lstatSync()` completes but before the subsequent access occurs. The symlink check therefore does not guarantee that the object eventually read is the same object that was validated. If the race succeeds, `readFileSync()` follows the substituted symlink and reads its target. The resulting bytes are Base64-encoded, embedded in the generated Worker, sent to Cloudflare, and potentially made publicly available through the deployed `workers.dev` site. ### Attack Path 1. The victim invokes the deployment script on a static directory that the attacker or another untrusted process can modify. 2. The attacker places an ordinary file in the directory so that `lstatSync()` reports a regular file. 3. Immediately after validation, the attacker replaces that file with a symbolic link targeting a sensi ...[truncated 1254 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Open each candidate file using no-follow semantics, such as `O_NOFOLLOW` where supported, rather than reopening it solely by pathname after validation. 2. Call `fstat` on the opened file descriptor and verify that it refers to a regular file before reading. 3. Read file contents directly from the validated descriptor so that pathname substitution cannot redirect the read. 4. For directory traversal, use descriptor-relative APIs where available and reject symlinks at every path component. 5. Compare device and inode information between validation and access if platform limitations require separate operations, and abort when they differ. 6. Require the deployment directory to be owned by or exclusively writable by the invoking user. Warn or refuse deployment when the tree is writable by untrusted users. 7. Consider copying validated inputs into a newly created private staging directory, then generate and upload the Worker only from that immutable snapshot. 8. Preserve the existing regular-file, symlink, file-count, root `index.html`, and generated-size checks after implementing descriptor-safe traversal. ]]>
