HIGHBroken authorization on invoice download
routes/invoice.ts:47VULK-1042CWE-639
The handler looks the invoice up by the id in the path and returns the file. Nothing scopes that lookup to req.org, and requireAuth only proves the caller is signed in, not that the invoice belongs to them. Any authenticated user can download invoices from another organization by changing the number in the URL. The check belongs in the query, not in the response.
Suggested change
| Line before | Line after | Change | Source |
|---|---|---|---|
| @@ -45,7 +45,11 @@ router.get('/invoices/:id/download') | |||
| 45 | 45 | router.get('/invoices/:id/download', requireAuth, async (req, res) => { | |
| 46 | Removed line. const invoice = await db.invoice.findUnique({ | ||
| 47 | Removed line. where: {id: req.params.id}, | ||
| 46 | Added line. const invoice = await db.invoice.findFirst({ | ||
| 47 | Added line. where: {id: req.params.id, orgId: req.org.id}, | ||
| 48 | 48 | }); | |
| 49 | 49 | ||
| 50 | Added line. if (!invoice) { | ||
| 51 | Added line. return res.status(404).send('not found'); | ||
| 52 | Added line. } | ||
| 53 | Added line. | ||
| 50 | 54 | return res.download(invoice.path); | |
| 51 | 55 | }); | |
vulkro scan · exit 1 · 1 high, 0 criticalRan on your runner. The code never left it.