apply security fix
Build & Push / Build & Push Docker Image (push) Failing after 3m46s

This commit is contained in:
root
2026-07-19 22:22:33 -04:00
parent ec03e783e5
commit a010ad6811
41 changed files with 626 additions and 224 deletions
+88
View File
@@ -0,0 +1,88 @@
import type { Request, Response, NextFunction } from 'express'
const MUTATING_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])
const SESSION_COOKIE_PATTERN = /(?:^|;\s*)(?:admin_session|employee_session|renter_session)=/
function configuredOrigins() {
return [
process.env.DASHBOARD_URL,
process.env.ADMIN_URL,
process.env.CARPLACE_URL,
process.env.WEBSITE_URL,
process.env.NEXT_PUBLIC_DASHBOARD_URL,
process.env.NEXT_PUBLIC_ADMIN_URL,
process.env.NEXT_PUBLIC_CARPLACE_URL,
process.env.NEXT_PUBLIC_WEBSITE_URL,
process.env.CORS_ORIGINS,
]
.flatMap((value) => (value ?? '').split(','))
.map((value) => value.trim())
.filter(Boolean)
}
function normalizeOrigin(value: string | undefined): string | null {
if (!value) return null
try {
const url = new URL(value)
return url.origin
} catch {
return null
}
}
function originFromReferer(value: string | undefined): string | null {
if (!value) return null
try {
return new URL(value).origin
} catch {
return null
}
}
function isAllowedDevelopmentOrigin(origin: string) {
if (process.env.NODE_ENV === 'production') return false
try {
const url = new URL(origin)
return url.protocol === 'http:' && ['localhost', '127.0.0.1'].includes(url.hostname)
} catch {
return false
}
}
export function isTrustedBrowserOrigin(origin: string | null) {
if (!origin) return false
const allowed = new Set(configuredOrigins().map(normalizeOrigin).filter((value): value is string => Boolean(value)))
return allowed.has(origin) || isAllowedDevelopmentOrigin(origin)
}
function isCookieAuthenticatedBrowserMutation(req: Request) {
if (!MUTATING_METHODS.has(req.method.toUpperCase())) return false
const cookie = req.headers.cookie ?? ''
if (!SESSION_COOKIE_PATTERN.test(cookie)) return false
const secFetchSite = Array.isArray(req.headers['sec-fetch-site'])
? req.headers['sec-fetch-site'][0]
: req.headers['sec-fetch-site']
if (secFetchSite && secFetchSite !== 'same-origin' && secFetchSite !== 'same-site' && secFetchSite !== 'none') return true
// Browser cookie-authenticated mutations must present Origin. Referer is a
// fallback for older clients only; API clients should use Bearer tokens.
return true
}
export function requireTrustedOriginForCookieMutations(req: Request, res: Response, next: NextFunction) {
if (!isCookieAuthenticatedBrowserMutation(req)) return next()
const origin = normalizeOrigin(req.headers.origin as string | undefined)
?? originFromReferer(req.headers.referer as string | undefined)
if (!isTrustedBrowserOrigin(origin)) {
return res.status(403).json({
error: 'csrf_origin_rejected',
message: 'Mutating cookie-authenticated requests must come from a trusted application origin.',
statusCode: 403,
})
}
next()
}