mirror of
https://github.com/honojs/hono.git
synced 2024-11-29 17:46:30 +01:00
b143c4ed8b
* feat(middleware/csrf): Introduce CSRF middleware * test(middleware/csrf): Add tests for CSRF middleware * chore: denoify * fix(middleware/csrf): fix typo.
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import type { Context } from '../../context.ts'
|
|
import { HTTPException } from '../../http-exception.ts'
|
|
import type { MiddlewareHandler } from '../../types.ts'
|
|
|
|
type IsAllowedOriginHandler = (origin: string, context: Context) => boolean
|
|
interface CSRFOptions {
|
|
origin?: string | string[] | IsAllowedOriginHandler
|
|
}
|
|
|
|
const isSafeMethodRe = /^(GET|HEAD)$/
|
|
const isRequestedByFormElementRe =
|
|
/^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/
|
|
|
|
export const csrf = (options?: CSRFOptions): MiddlewareHandler => {
|
|
const handler: IsAllowedOriginHandler = ((optsOrigin) => {
|
|
if (!optsOrigin) {
|
|
return (origin, c) => origin === new URL(c.req.url).origin
|
|
} else if (typeof optsOrigin === 'string') {
|
|
return (origin) => origin === optsOrigin
|
|
} else if (typeof optsOrigin === 'function') {
|
|
return optsOrigin
|
|
} else {
|
|
return (origin) => optsOrigin.includes(origin)
|
|
}
|
|
})(options?.origin)
|
|
const isAllowedOrigin = (origin: string | undefined, c: Context) => {
|
|
if (origin === undefined) {
|
|
// denied always when origin header is not present
|
|
return false
|
|
}
|
|
return handler(origin, c)
|
|
}
|
|
|
|
return async function cors(c, next) {
|
|
if (
|
|
!isSafeMethodRe.test(c.req.method) &&
|
|
isRequestedByFormElementRe.test(c.req.header('content-type') || '') &&
|
|
!isAllowedOrigin(c.req.header('origin'), c)
|
|
) {
|
|
const res = new Response('Forbidden', {
|
|
status: 403,
|
|
})
|
|
throw new HTTPException(403, { res })
|
|
}
|
|
|
|
await next()
|
|
}
|
|
}
|