BUG: Upload routes missing authentication — requireAuth imported but never used #86

Closed
opened 2026-05-25 18:47:12 -04:00 by zgaetano · 0 comments
Owner

Description

All upload routes in services/mam-api/src/routes/upload.js are completely unauthenticated — requireAuth is imported at the top of the file as dead code but never applied to any route handler.

The file imports requireAuth:

import requireAuth from '../middleware/requireAuth.js';

But none of the route handlers use it:

router.post('/init', async (req, res, next) => { ... });   // ← no requireAuth
router.post('/part', upload.single('file'), async ...);     // ← no requireAuth
router.post('/complete', async (req, res, next) => { ... });// ← no requireAuth
router.post('/abort', async (req, res, next) => { ... });   // ← no requireAuth
router.post('/simple', upload.single('file'), async ...);   // ← no requireAuth

The parent app mount in index.js also doesn't add auth at the group level:

app.use('/api/v1/upload', uploadRouter);
//  vs
app.use('/api/v1/assets', requireAuth, assetsRouter);

Impact (when AUTH_ENABLED=true):

  • Anyone who discovers the server URL can upload arbitrary files to the S3 bucket without logging in
  • Anyone can read POST /upload/init responses to discover upload IDs and keys
  • The /abort endpoint can delete in-progress asset rows if an attacker knows the uploadId/key/assetId

Mitigations that make exploitation less trivial:

  • /init requires knowing a valid projectId (UUID)
  • Part and complete calls need the unguessable uploadId (S3-generated)
  • Simple upload also needs a valid projectId

Suggested fix: Add requireAuth to each route or add router.use(requireAuth) at the top of the router. Since the XHR-based upload flow (_xhrPost in screens-ingest.jsx) does not send auth headers, the frontend's _xhrPost would also need to include the Authorization: Bearer header to match. (The existing window.ZAMPP_API.fetch helper already handles this — it's just that the multipart XHR flow bypasses it.)

## Description All upload routes in `services/mam-api/src/routes/upload.js` are completely unauthenticated — `requireAuth` is imported at the top of the file as dead code but never applied to any route handler. **The file imports `requireAuth`:** ```js import requireAuth from '../middleware/requireAuth.js'; ``` **But none of the route handlers use it:** ```js router.post('/init', async (req, res, next) => { ... }); // ← no requireAuth router.post('/part', upload.single('file'), async ...); // ← no requireAuth router.post('/complete', async (req, res, next) => { ... });// ← no requireAuth router.post('/abort', async (req, res, next) => { ... }); // ← no requireAuth router.post('/simple', upload.single('file'), async ...); // ← no requireAuth ``` The parent app mount in `index.js` also doesn't add auth at the group level: ```js app.use('/api/v1/upload', uploadRouter); // vs app.use('/api/v1/assets', requireAuth, assetsRouter); ``` **Impact (when `AUTH_ENABLED=true`):** - Anyone who discovers the server URL can upload arbitrary files to the S3 bucket without logging in - Anyone can read `POST /upload/init` responses to discover upload IDs and keys - The `/abort` endpoint can delete in-progress asset rows if an attacker knows the uploadId/key/assetId **Mitigations that make exploitation less trivial:** - `/init` requires knowing a valid `projectId` (UUID) - Part and complete calls need the unguessable uploadId (S3-generated) - Simple upload also needs a valid `projectId` **Suggested fix:** Add `requireAuth` to each route or add `router.use(requireAuth)` at the top of the router. Since the XHR-based upload flow (`_xhrPost` in `screens-ingest.jsx`) does not send auth headers, the frontend's `_xhrPost` would also need to include the `Authorization: Bearer` header to match. (The existing `window.ZAMPP_API.fetch` helper already handles this — it's just that the multipart XHR flow bypasses it.)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: WildDragonLLC/dragonflight#86
No description provided.