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. // Same-origin and curl send no Origin header at all.
if (!origin) return callback(null, true) if (!origin) return callback(null, true)
if (allowedOrigins.includes(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'], methods: ['POST'],
allowedHeaders: ['Content-Type'], allowedHeaders: ['Content-Type'],
@ -51,8 +56,15 @@ app.use('/api/audit-request', auditRouter)
// Keep internals out of the response body. // Keep internals out of the response body.
app.use((error, _req, res, _next) => { app.use((error, _req, res, _next) => {
console.error('Unhandled error:', error.message) const status = error.status ?? 500
res.status(500).json({ success: false, message: 'Internal server error.' }) 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')) const port = Number(env('PORT', '3001'))