T09 · Insecure Skill Coding Practices
- Location
deploy.js:109- Finding
Build output path traversal can publish files outside the project
- Content
View full analysis
Vulnerability Details
File Location:
deploy.js:109-112,deploy.js:492-496,deploy.js:525-527
Vulnerability Type: Unrestricted deployment path / path traversal
Risk Level: HighVulnerable Code
js pagesBuildOutputDir: process.env.PAGES_BUILD_OUTPUT_DIR || fileConfig.pagesBuildOutputDir || DEFAULTS.pagesBuildOutputDirjs const buildOutputDir = config.pagesBuildOutputDir || '.'; const buildPath = path.resolve(projectPath, buildOutputDir); if (!fs.existsSync(buildPath)) { error(`Build output directory does not exist: ${buildPath}`); }js ['pages', 'deploy', buildOutputDir, `--project-name=${name}`, `--branch=${branch}`]Technical Analysis
The build output directory is accepted from an environment variable or configuration file without enforcing that it remains inside the selected project directory.
path.resolve(projectPath, buildOutputDir)permits both absolute paths and parent-directory traversal such as../.The code only verifies that the resolved path exists. It does not:
- Verify that the path is a directory.
- Confirm that it is contained within
projectPath. - Reject absolute paths.
- Detect symbolic links that resolve outside the project.
- Pass the validated canonical path to Wrangler.
Consequently, Wrangler can be instructed to deploy an arbitrary directory that the current operating-system user can read.
Attack Path
- An attacker influences the environment or deployment configuration, or convinces the operator to use an unsafe configuration.
- The attacker sets
PAGES_BUILD_OUTPUT_DIRorpagesBuildOutputDirto a value such as../,../../sensitive-directory, or an absolute path. path.resolve()resolves the value outside the intended project.- The existence check succeeds because the external path exists.
- The untrusted path is passed to
wrangler pages deploy. - Wrangler uploads files from that directory to Cloudflare Pages, making them remotely accessible.
...[truncated 633 chars]
- Remediation
View remediation
Remediation Suggestions
- Resolve and canonicalize both the project path and requested output path:
js const projectRoot = fs.realpathSync(projectPath); const requestedPath = path.resolve(projectRoot, buildOutputDir); const outputPath = fs.realpathSync(requestedPath); const relative = path.relative(projectRoot, outputPath); if ( relative === '' || (!relative.startsWith('..' + path.sep) && !path.isAbsolute(relative)) ) { // Path is contained within the project. } else { error('Build output directory must be inside the project directory'); }- Use
fs.statSync(outputPath).isDirectory()to require a directory. - Reject absolute configuration values unless there is a documented, explicitly approved use case.
- Resolve symbolic links before performing the containment check.
- Pass the validated
outputPath, rather than the original untrusted value, to Wrangler. - Display the canonical directory and require explicit confirmation before deployment.
- Prefer a fixed build directory such as
distorpublicover deploying arbitrary configurable paths.
