Files
SnowLuma/.pnpm-store/v11/files/0d/77b9e0ee978249253ba811a5fb04229f1599b2a0c71b351fb08adca38832b55e07730fa143e6f04025a4fd6acbb8d9cc1891b470d4ee081093d827c1e98ae6
TanTan 2a69b23b47
Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

107 lines
4.3 KiB
Plaintext

Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_http_exception = require("../../http-exception.js");
//#region src/middleware/csrf/index.ts
const secFetchSiteValues = [
"same-origin",
"same-site",
"none",
"cross-site"
];
const isSecFetchSite = (value) => secFetchSiteValues.includes(value);
const isSafeMethodRe = /^(GET|HEAD|OPTIONS)$/;
const isRequestedByFormElementRe = /^\b(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)\b/i;
/**
* CSRF Protection Middleware for Hono.
*
* Protects against Cross-Site Request Forgery attacks by validating request origins
* and sec-fetch-site headers. The request is allowed if either validation passes.
*
* @see {@link https://hono.dev/docs/middleware/builtin/csrf}
*
* @param {CSRFOptions} [options] - The options for the CSRF protection middleware.
* @param {string|string[]|(origin: string, context: Context) => boolean} [options.origin] -
* Allowed origins for requests.
* - string: Single allowed origin (e.g., 'https://example.com')
* - string[]: Multiple allowed origins
* - function: Custom validation logic
* - Default: Only same origin as the request URL
* @param {string|string[]|(secFetchSite: string, context: Context) => boolean} [options.secFetchSite] -
* Sec-Fetch-Site header validation. Standard values include 'same-origin', 'same-site', 'cross-site', 'none'.
* - string: Single allowed value (e.g., 'same-origin')
* - string[]: Multiple allowed values (e.g., ['same-origin', 'same-site'])
* - function: Custom validation with access to context
* - Default: Only allows 'same-origin'
* @returns {MiddlewareHandler} The middleware handler function.
*
* @example
* ```ts
* const app = new Hono()
*
* // Default: both origin and sec-fetch-site validation
* app.use('*', csrf())
*
* // Allow specific origins
* app.use('*', csrf({ origin: 'https://example.com' }))
* app.use('*', csrf({ origin: ['https://app.com', 'https://api.com'] }))
*
* // Allow specific sec-fetch-site values
* app.use('*', csrf({ secFetchSite: 'same-origin' }))
* app.use('*', csrf({ secFetchSite: ['same-origin', 'same-site'] }))
*
* // Dynamic sec-fetch-site validation
* app.use('*', csrf({
* secFetchSite: (secFetchSite, c) => {
* // Always allow same-origin
* if (secFetchSite === 'same-origin') return true
* // Allow cross-site for webhook endpoints
* if (secFetchSite === 'cross-site' && c.req.path.startsWith('/webhook/')) {
* return true
* }
* return false
* }
* }))
*
* // Dynamic origin validation
* app.use('*', csrf({
* origin: (origin, c) => {
* // Allow same origin
* if (origin === new URL(c.req.url).origin) return true
* // Allow specific trusted domains
* return ['https://app.example.com', 'https://admin.example.com'].includes(origin)
* }
* }))
* ```
*/
const csrf = (options) => {
const originHandler = ((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 = async (origin, c) => {
if (origin === void 0) return false;
return await originHandler(origin, c);
};
const secFetchSiteHandler = ((optsSecFetchSite) => {
if (!optsSecFetchSite) return (secFetchSite) => secFetchSite === "same-origin";
else if (typeof optsSecFetchSite === "string") return (secFetchSite) => secFetchSite === optsSecFetchSite;
else if (typeof optsSecFetchSite === "function") return optsSecFetchSite;
else return (secFetchSite) => optsSecFetchSite.includes(secFetchSite);
})(options?.secFetchSite);
const isAllowedSecFetchSite = async (secFetchSite, c) => {
if (secFetchSite === void 0) return false;
if (!isSecFetchSite(secFetchSite)) return false;
return await secFetchSiteHandler(secFetchSite, c);
};
return async function csrf(c, next) {
if (!isSafeMethodRe.test(c.req.method) && isRequestedByFormElementRe.test(c.req.header("content-type") || "text/plain") && !await isAllowedSecFetchSite(c.req.header("sec-fetch-site"), c) && !await isAllowedOrigin(c.req.header("origin"), c)) {
const res = new Response("Forbidden", { status: 403 });
throw new require_http_exception.HTTPException(403, { res });
}
await next();
};
};
//#endregion
exports.csrf = csrf;