Skip to content

fix(security): patch path traversal and symlink in plugin upload (#719) - #614

Open
tri2510 wants to merge 2 commits into
eclipse-autowrx:mainfrom
tri2510:security/plugin-upload-path-traversal
Open

fix(security): patch path traversal and symlink in plugin upload (#719)#614
tri2510 wants to merge 2 commits into
eclipse-autowrx:mainfrom
tri2510:security/plugin-upload-path-traversal

Conversation

@tri2510

@tri2510 tri2510 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes three security vulnerabilities in POST /v2/plugin/upload/:slug reported in issue #719.

Vulnerabilities Fixed

# Vulnerability CWE Fix
1 Path traversal via slug — slug was validated only as Joi.string().required(), allowing URL-encoded ../ sequences to extract the zip into arbitrary directories (e.g. backend/src/ → RCE on next process restart) CWE-22 Apply the existing slug custom validator (rejects non-slug characters) + add path.resolve containment check in the controller (defense in depth)
2 Symlink-based arbitrary file readspawn('unzip') recreated symbolic links, and express.static followed them, allowing read access to any file on disk (e.g. .env containing JWT_SECRET) CWE-59 Replace spawn('unzip') with yauzl-based safeExtractZip() that rejects symlink entries, absolute paths, and ../ in entry names. Add dotfiles: 'ignore' to express.static mounts for /plugin
3 Missing authorization — the admin checkPermission guard was commented out, and the ownership check ran after extraction had already completed CWE-862 Move ownership check before extraction so files are never written for unauthorized users. Remove commented-out checkPermission and redundant auth() from route (auth already applied via router.use(auth()) at line 26)

Files Changed

  • backend/src/validations/plugin.validation.js — apply slug custom validator to uploadInternal.params.slug
  • backend/src/controllers/plugin.controller.js — replace spawn('unzip') with safeExtractZip(), add path containment check, move ownership check before extraction
  • backend/src/routes/v2/system/plugin.route.js — remove commented-out checkPermission and redundant auth()
  • backend/src/app.js — add dotfiles: 'ignore' to express.static mounts for /plugin and /static/plugin
  • backend/package.json / backend/yarn.lock — add yauzl dependency

Test plan

  • Existing plugin upload flow still works (upload zip → extract → serve at /plugin/<slug>/index.js)
  • ../ in slug is rejected by validation (e.g. ..%2F..%2Fsrc → 400)
  • Zip containing symlink entries is rejected with 400
  • Upload to existing slug owned by another user returns 403 without extracting any files
  • yarn lint passes on changed files
  • Backend unit tests pass (no new failures vs. baseline)

Closes #719

tri2510 added 2 commits August 7, 2026 10:36
Fixes three vulnerabilities in POST /v2/plugin/upload/:slug:

1. Path traversal via slug (CWE-22): slug was validated only as
   Joi.string().required(), allowing URL-encoded ../ sequences to
   extract the zip into arbitrary directories (e.g. backend/src/ → RCE).
   - Apply the existing slug custom validator (rejects non-slug chars)
   - Add path.resolve containment check in the controller (defense in depth)

2. Symlink-based arbitrary file read (CWE-59): spawn('unzip') recreated
   symbolic links, and express.static followed them, allowing read
   access to any file (e.g. .env containing JWT_SECRET).
   - Replace spawn('unzip') with safe yauzl-based extraction that rejects
     symlink entries, absolute paths, and ../ in entry names
   - Add dotfiles: 'ignore' to express.static mounts for /plugin

3. Missing authorization (CWE-862): the admin checkPermission guard
   was commented out, and the ownership check ran after extraction.
   - Move ownership check before extraction so files are never written
     for unauthorized users
   - Remove commented-out checkPermission and redundant auth() from route
     (auth() already applied via router.use(auth()) at line 26)
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