Skip to content

Fix path traversal in file deletion (CVE-2026-45230)#136

Open
YoyoChaud wants to merge 1 commit into
DumbWareio:mainfrom
YoyoChaud:fix/cve-2026-45230
Open

Fix path traversal in file deletion (CVE-2026-45230)#136
YoyoChaud wants to merge 1 commit into
DumbWareio:mainfrom
YoyoChaud:fix/cve-2026-45230

Conversation

@YoyoChaud
Copy link
Copy Markdown

@YoyoChaud YoyoChaud commented May 16, 2026

The /api/delete-file endpoint and deleteAssetFileAsync() resolved user-supplied paths with path.join() without verifying the result stayed inside the base directory. A request like /../../../tmp/x could reach any file the Node process could touch.

Introduce resolveDataFilePath() that:

  • resolves against DATA_DIR via path.resolve (which normalizes ../)
  • rejects anything that doesn't stay under DATA_DIR
  • additionally restricts to the legitimate upload subdirectories (Images/, Receipts/, Manuals/) so Assets.json, SubAssets.json, config.json and other in-DATA_DIR files can never be targeted

deleteAssetFileAsync() now skips silently (with a [SECURITY] log) when the path is rejected, matching the existing ENOENT-tolerant behavior used by PUT /api/assets/:id and PUT /api/subassets/:id filesToDelete.

/api/delete-file returns 400 on rejection. As a side effect the endpoint is also realigned with the on-disk layout (uploads live under DATA_DIR, not __dirname) which the previous implementation got wrong; no internal client used it so no callers break.

High-level PR Summary

This PR fixes a critical path traversal vulnerability (CVE-2026-45230) in the file deletion functionality. The previous implementation used path.join() without validation, allowing attackers to delete arbitrary files outside the intended directories using paths like /../../../tmp/x. The fix introduces a new resolveDataFilePath() function that resolves paths against DATA_DIR, normalizes path traversal attempts, and restricts access to only legitimate upload subdirectories (Images/, Receipts/, Manuals/). Both the deleteAssetFileAsync() function and the /api/delete-file endpoint now use this validation, with the former logging security warnings and continuing silently (matching existing error-tolerant behavior) while the latter returns a 400 error for invalid paths.

⏱️ Estimated Review Time: 15-30 minutes

💡 Review Order Suggestion
Order File Path
1 server.js

Need help? Join our Discord

Summary by CodeRabbit

Bug Fixes

  • Improved file deletion security with stricter validation mechanisms. The system now properly validates all file operations and rejects invalid requests with appropriate error responses. Unauthorized access attempts are detected, logged, and prevented to enhance data protection and system reliability.

Review Change Stack

The /api/delete-file endpoint and deleteAssetFileAsync() resolved
user-supplied paths with path.join() without verifying the result stayed
inside the base directory. A request like /../../../tmp/x could reach
any file the Node process could touch.

Introduce resolveDataFilePath() that:
  - resolves against DATA_DIR via path.resolve (which normalizes ../)
  - rejects anything that doesn't stay under DATA_DIR
  - additionally restricts to the legitimate upload subdirectories
    (Images/, Receipts/, Manuals/) so Assets.json, SubAssets.json,
    config.json and other in-DATA_DIR files can never be targeted

deleteAssetFileAsync() now skips silently (with a [SECURITY] log) when
the path is rejected, matching the existing ENOENT-tolerant behavior
used by PUT /api/assets/:id and PUT /api/subassets/:id filesToDelete.

/api/delete-file returns 400 on rejection. As a side effect the endpoint
is also realigned with the on-disk layout (uploads live under DATA_DIR,
not __dirname) which the previous implementation got wrong; no internal
client used it so no callers break.
Copilot AI review requested due to automatic review settings May 16, 2026 09:18
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b03de819-9fb8-48f4-b6fe-75f89b0070a0

📥 Commits

Reviewing files that changed from the base of the PR and between 2bacca1 and 22363e6.

📒 Files selected for processing (1)
  • server.js

Walkthrough

This PR adds a centralized resolveDataFilePath() function that validates file paths are within whitelisted subdirectories (Images, Receipts, Manuals), then plugs it into file deletion operations. The /api/delete-file endpoint and deleteAssetFileAsync() now reject path traversal attempts with logged warnings and HTTP 400 responses instead of blindly constructing filesystem paths from untrusted input.

Changes

File Deletion Path Traversal Hardening

Layer / File(s) Summary
Path resolver and deletion integration
server.js
New resolveDataFilePath() function validates paths stay within allowed subdirectories and is integrated into deleteAssetFileAsync() and /api/delete-file endpoint, replacing unsafe direct path construction with allowlist-based resolution that short-circuits invalid traversal attempts.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🛡️ Path traversal plots defeated by a simple guard,
An allowlist keeps deletion safe and hard,
No more ../ tricks to escape the fold,
Just Images, Receipts, and Manuals to hold,
Your files are locked down—pretty neat for once!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: fixing a path traversal vulnerability (CVE-2026-45230) in file deletion functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Hardens two file-deletion code paths against path traversal by introducing a shared resolveDataFilePath helper that confines deletions to a fixed allow-list of subdirectories under DATA_DIR.

Changes:

  • Adds ALLOWED_ASSET_FILE_SUBDIRS and resolveDataFilePath() helper to validate/resolve incoming asset paths.
  • Updates deleteAssetFileAsync to use the helper and silently skip on rejection (with a security log).
  • Updates POST /api/delete-file to use the helper and return 400 on rejection.

Comment thread server.js
const { path: filePath } = req.body;
if (!filePath) return res.status(400).json({ error: 'No file path provided' });
const absPath = path.join(__dirname, filePath.startsWith('/') ? filePath.substring(1) : filePath);
const absPath = resolveDataFilePath(filePath);
Comment thread server.js

function resolveDataFilePath(filePath) {
if (typeof filePath !== 'string') return null;
const cleanPath = filePath.replace(/^\/+/, '');
Comment thread server.js
Comment on lines +500 to +508

function resolveDataFilePath(filePath) {
if (typeof filePath !== 'string') return null;
const cleanPath = filePath.replace(/^\/+/, '');
if (!cleanPath) return null;
const resolved = path.resolve(DATA_DIR, cleanPath);
const dataDirPrefix = DATA_DIR + path.sep;
if (resolved !== DATA_DIR && !resolved.startsWith(dataDirPrefix)) return null;
const relative = path.relative(DATA_DIR, resolved);
Comment thread server.js
Comment on lines +524 to +525
console.warn(`[SECURITY] Path traversal blocked in deleteAssetFileAsync: ${filePath}`);
return resolve();
Comment thread server.js

// Subdirectories under DATA_DIR that legitimately hold uploaded asset files.
// Any path resolved outside one of these is rejected to prevent path traversal.
const ALLOWED_ASSET_FILE_SUBDIRS = ['Images', 'Receipts', 'Manuals'];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants