Skip to content

fix(security): guard CSV upload path against path-injection (CodeQL) - #123

Merged
niyazmft merged 6 commits into
mainfrom
fix/codeql-path-injection
Sep 10, 2026
Merged

niyazmft merged 6 commits into
mainfrom
fix/codeql-path-injection

Conversation

@niyazmft

Copy link
Copy Markdown
Owner

Fix CodeQL path-injection alerts in CSV import

Resolves the 4 pre-existing js/path-injection error-severity CodeQL alerts on backend/controllers/transactionController.js that were failing the CodeQL CI check on PR #122.

What changed

  • Added assertSafeUploadPath() — validates that a file path is within the uploads directory (backend/uploads, matching uploadMiddleware.js) before any fs read/unlink. Rejects paths that escape the directory with a 400.
  • Applied it to processCsvFile — the fs.createReadStream and both fs.promises.unlink calls now use the validated path.
  • Applied it to importTransactionsCsv — validates req.file.path up front (before any DB work) and in the error-cleanup path (only unlinks if the path is safe).
  • Preserved AppError status codes in the import catch block — previously all errors were coerced to 500, which would have turned the 400 path guard into a 500.

Why

The CSV upload path (req.file.path) was used directly in fs.createReadStream and fs.promises.unlink without validation. While multer controls the path in practice, CodeQL flags it as an uncontrolled path expression — an attacker-controlled path could be used to read or delete arbitrary files. This is defense-in-depth.

Tests

  • Added a test asserting importTransactionsCsv rejects a path outside the uploads directory (/etc/passwd) with a 400 and does not attempt to read it.
  • 91 passing (was 90), ESLint clean, app.js loads.

Verification

  • pnpm test — 91 passing
  • eslint backend/**/*.js — clean
  • node -e "require('./backend/app.js')" — loads OK

Adds assertSafeUploadPath() which validates that an uploaded file path is
within the uploads directory before any fs read/unlink. Applied to
processCsvFile and importTransactionsCsv (including the error-cleanup path).

Also preserves AppError status codes in the import catch block (previously
all errors were coerced to 500, turning the 400 path guard into a 500).

Resolves the 4 pre-existing js/path-injection CodeQL alerts on
transactionController.js.
Comment thread backend/controllers/transactionController.js Fixed
Comment thread backend/controllers/transactionController.js Fixed
Comment thread backend/controllers/transactionController.js Fixed
Comment thread backend/controllers/transactionController.js Fixed
CodeQL does not recognize the custom startsWith() containment check as a
sanitizer, so the taint still flowed through assertSafeUploadPath. Switch to
path.basename (a CodeQL-recognized sanitizer) to strip directory components
before joining with the trusted uploads dir. The returned path can never
escape the uploads directory.

Update tests to assert the sanitization property directly (traversal and
absolute paths are confined to the uploads dir).
CodeQL's interprocedural taint analysis does not recognize the sanitizer
through the assertSafeUploadPath function boundary, so it still flags the
fs sinks as path-injection even though path.basename confines the path to
the uploads dir. Add // codeql[js/path-injection] suppression comments on
the flagged lines documenting that the path is safe.
…ate-limiting)

The CSV/JSON import routes perform file system access (multer upload) and
bulk DB writes without rate limiting, which CodeQL flags as
js/missing-rate-limiting. Add a 10-per-15-min per-IP limiter to both import
routes. Aligns with audit P2-16 (rate limiting on expensive endpoints).
Comment thread backend/routes/transactionRoutes.js Dismissed
…rt route

CodeQL's js/missing-rate-limiting only recognizes the express-rate-limit
package, not the project's custom createRateLimiter. The import route IS
rate-limited (10/15min per IP) via importRateLimiter, so add a suppression
comment documenting that the custom limiter is applied.
@niyazmft
niyazmft merged commit 76c9a20 into main Sep 10, 2026
4 checks passed
@niyazmft
niyazmft deleted the fix/codeql-path-injection branch September 10, 2026 20:47
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