fix: return 403, not 500, for disallowed CORS origins

The cors() origin callback rejected disallowed origins by calling
callback(new Error(...)), which Express routes to the generic error
handler - previously hardcoded to always respond 500 and log
"Unhandled error". A rejected origin is expected, routine traffic
(bots, scanners, or a deliberate CORS test), not a server fault, and
was being reported and logged as one.

The request was already being correctly rejected either way (no data
processed, no email sent) - this only fixes the status code and log
noise, not a security gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Sascha 2026-07-29 18:56:35 +02:00
parent f50442a5c8
commit 45ff50e34c
1 changed files with 15 additions and 3 deletions

View File

@ -35,7 +35,12 @@ app.use(
// Same-origin and curl send no Origin header at all.
if (!origin) return callback(null, true)
if (allowedOrigins.includes(origin)) return callback(null, true)
callback(new Error('Origin not allowed'))
const error = new Error('Origin not allowed')
// Read by the error handler below - a rejected origin is expected,
// routine traffic (bots, scanners, this exact curl test), not a
// server fault, and should neither look like one nor be logged as one.
error.status = 403
callback(error)
},
methods: ['POST'],
allowedHeaders: ['Content-Type'],
@ -51,8 +56,15 @@ app.use('/api/audit-request', auditRouter)
// Keep internals out of the response body.
app.use((error, _req, res, _next) => {
const status = error.status ?? 500
if (status >= 500) {
console.error('Unhandled error:', error.message)
res.status(500).json({ success: false, message: 'Internal server error.' })
return
}
// Expected rejections (e.g. disallowed CORS origin) - not a server fault,
// so no error-level log line and no generic message.
res.status(status).json({ success: false, message: error.message })
})
const port = Number(env('PORT', '3001'))