Skip to content

fix: confine database restore paths to the data directory - #14

Draft
P3RF3CTION wants to merge 1 commit into
mainfrom
security/restore-path-traversal
Draft

fix: confine database restore paths to the data directory#14
P3RF3CTION wants to merge 1 commit into
mainfrom
security/restore-path-traversal

Conversation

@P3RF3CTION

Copy link
Copy Markdown
Owner

Overview

Follow-up to the security-hardening PR, addressing the Snyk Code (SAST) path-traversal findings. Of the ten flagged, one is real and is fixed here; the other nine are false positives the scanner cannot classify, explained below so they can be dismissed with a reason.

The real one: /database/restore

backupPath and targetPath arrive straight from the request body and become fs read/write targets — a "backup" is read and a "target" is written wherever the strings point, with no confinement. The route is requireAdmin, but admin-only is not a boundary against traversal: an admin session is exactly what an attacker who lands one wants to turn into arbitrary file read/write on the host.

Backups only ever live under DATA_DIR, so both paths are now resolved against it and rejected if they escape — via .., an absolute path, a shared-prefix sibling (/app/data-evil vs /app/data), or a null byte. A new helper resolveWithinDir does the containment check, with tests covering each escape.

Behavior: no change for any legitimate restore, since real backups sit under DATA_DIR. No frontend calls this endpoint; it is an admin API.

The nine false positives

Documented here so they can be marked Ignore in Snyk with a rationale rather than left dangling:

  • Seven (fleet-routes.ts, content-routes.ts, download-routes.ts) are sftp.createReadStream / sftp.createWriteStream from ssh2. They read and write on the remote managed host over SFTP — that is the feature — not on the Termix filesystem. Snyk cannot tell an SFTP stream from a local fs path.
  • Two (database.ts, the upload path) operate on req.file.path from multer, whose filename already runs through path.basename in the disk-storage layer (added in the previous security PR). The taint tracker does not see that sanitizer.

Verification

Backend tests  1733 passed | 1 skipped
tsc --noEmit   clean
lint           0 errors

New tests: resolveWithinDir against traversal, absolute paths, shared-prefix siblings, null bytes and non-string input.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UN1o1GH1wgG2KM4SJHiMs5


Generated by Claude Code

Snyk Code flags /database/restore for path traversal, and it is right:
backupPath and targetPath come straight from the request body and become
fs read and write targets with no confinement. The route is admin-only,
but that is not a boundary against traversal - an admin session is exactly
what an attacker who lands one turns into arbitrary file read/write on the
host, and restore reads a "backup" and writes a "target" wherever the
strings point.

Backups only ever live under DATA_DIR, so both paths are now resolved
against it and rejected if they escape - via .., an absolute path, a
shared-prefix sibling (/app/data-evil vs /app/data), or a null byte. New
helper resolveWithinDir with tests covering each escape.

The other nine Snyk Code path-traversal hits in this scan are not real:
seven are ssh2 sftp.createReadStream/createWriteStream, which write to the
remote managed host by design, not the Termix filesystem; the other two
are the upload path in database.ts, whose filename already runs through
path.basename in the multer storage layer. The scanner cannot tell an
SFTP stream or a sanitized name from a raw fs path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UN1o1GH1wgG2KM4SJHiMs5
@P3RF3CTION

Copy link
Copy Markdown
Owner Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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.

1 participant