{"version":3,"file":"index.mjs","names":["serialize","sign","createError","Tokens","verify","getCookieOptions","options","undefined","opts","key","path","value","Object","entries","defaultValue","req","body","_csrf","query","headers","getIgnoredMethods","methods","obj","method","toUpperCase","getSecretBag","sessionKey","cookie","cookieKey","signed","getSecret","bag","Error","setCookie","res","name","val","data","rawPrev","getHeader","prev","toString","header","Array","isArray","concat","setHeader","setSecret","secret","csrfSecret","verifyConfiguration","csurf","tokens","ignoreMethods","TypeError","ignoreMethod","next","token","csrfToken","sec","secretSync","create","ignoreRequest","code"],"sources":["../../src/index.ts"],"sourcesContent":["import { type SerializeOptions, serialize } from 'cookie';\n\nimport { sign } from 'cookie-signature';\nimport type { NextFunction, Request, Response } from 'express';\nimport createError from 'http-errors';\n\nimport Tokens, { type Options as TokensOptions, verify } from './tokens';\n\ndeclare global {\n  // eslint-disable-next-line @typescript-eslint/no-namespace\n  namespace Express {\n    // eslint-disable-next-line @typescript-eslint/consistent-type-definitions\n    interface Request {\n      // The CSRF token generation routine, added by this library.\n      csrfToken: () => string;\n\n      // Cookie-signature secret, configured and set by \"cookie-parser\" middleware,\n      // if that is configured to support signed cookies:\n      // https://github.com/expressjs/cookie-parser\n      secret?: string;\n    }\n  }\n}\n\n// TODO: This should come from a cookie library.\ntype CookieOptions = {\n  domain?: string;\n  httpOnly?: boolean;\n  key: string;\n  maxAge?: number;\n  path: string;\n  sameSite?: 'lax' | 'none' | 'strict' | true;\n  secure?: boolean;\n  signed?: boolean;\n};\n\nexport type Options = TokensOptions & {\n  cookie?: CookieOptions | true;\n  ignoreMethods?: string[];\n  ignoreRequest?: (req: Request) => boolean;\n  sessionKey?: string;\n  value?: (req: Request) => string;\n};\n\n/**\n * Get options for cookie.\n *\n * @param {boolean|object} [options]\n */\nfunction getCookieOptions(\n  options: Partial<CookieOptions> | boolean | undefined,\n): CookieOptions | undefined {\n  if (options !== true && typeof options !== 'object') {\n    return undefined;\n  }\n\n  const opts: CookieOptions = {\n    key: '_csrf',\n    path: '/',\n  };\n\n  if (typeof options === 'object') {\n    for (const [key, value] of Object.entries(options)) {\n      // TODO: It actually breaks one of existing tests, if we don't check\n      // for it. Perhaps, we should correct typings, or do some other refactoring\n      // to avoid this.\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n      if (value !== undefined) {\n        (opts[key as keyof CookieOptions] as unknown) = value;\n      }\n    }\n  }\n\n  return opts;\n}\n\n/**\n * Default value function, checking the `req.body`\n * and `req.query` for the CSRF token.\n *\n * @param req\n * @return\n */\nfunction defaultValue(req: Request<unknown, unknown, {\n  _csrf?: string;\n} | undefined, {\n  _csrf?: string;\n} | undefined>): string {\n  /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */\n  // eslint-disable-next-line no-underscore-dangle\n  return (req.body?._csrf || req.query?._csrf\n    || req.headers['csrf-token']\n    || req.headers['xsrf-token']\n    || req.headers['x-csrf-token']\n    || req.headers['x-xsrf-token']) as string;\n  /* eslint-enable @typescript-eslint/prefer-nullish-coalescing */\n}\n\n// TODO: Actually, we should type `methods` stricter, limiting it to the valid\n// method name literals.\n/**\n * Get a lookup of ignored methods.\n *\n * @param {array} methods\n * @returns {object}\n * @api private\n */\nfunction getIgnoredMethods(methods: string[]): Record<string, true> {\n  const obj: Record<string, true> = {};\n\n  for (const method of methods) {\n    obj[method.toUpperCase()] = true;\n  }\n\n  return obj;\n}\n\ntype SecretBag = Record<string, string>;\n\n/**\n * Get the token secret bag from the request.\n *\n * @param {IncomingMessage} req\n * @param {String} sessionKey\n * @param {Object} [cookie]\n * @api private\n */\nfunction getSecretBag(\n  req: Request,\n  sessionKey: string,\n  cookie: CookieOptions | undefined,\n): SecretBag | undefined {\n  if (cookie) {\n    // get secret from cookie\n    const cookieKey = cookie.signed\n      ? 'signedCookies'\n      : 'cookies';\n\n    return req[cookieKey] as SecretBag;\n  }\n\n  // TODO: A less forceful type casting would be nice to have here.\n  // get secret from session\n  return (req as unknown as Record<string, unknown>)[sessionKey] as SecretBag;\n}\n\n/**\n * Get the token secret from the request.\n *\n * @param {IncomingMessage} req\n * @param {String} sessionKey\n * @param {Object} [cookie]\n * @api private\n */\nfunction getSecret(\n  req: Request,\n  sessionKey: string,\n  cookie: CookieOptions | undefined,\n) {\n  // get the bag & key\n  const bag = getSecretBag(req, sessionKey, cookie);\n  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n  const key = cookie?.key || 'csrfSecret';\n\n  if (!bag) {\n    throw new Error('misconfigured csrf');\n  }\n\n  // return secret from bag\n  return bag[key];\n}\n\n/**\n * Set a cookie on the HTTP response.\n *\n * @param {OutgoingMessage} res\n * @param {string} name\n * @param {string} val\n * @param {Object} [options]\n * @api private\n */\nfunction setCookie(\n  res: Response,\n  name: string,\n  val: string,\n  options: SerializeOptions,\n) {\n  const data = serialize(name, val, options);\n  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n  const rawPrev = res.getHeader('set-cookie') || [];\n  const prev = typeof rawPrev === 'number' ? rawPrev.toString() : rawPrev;\n  const header = Array.isArray(prev) ? prev.concat(data)\n    : [prev, data];\n\n  res.setHeader('set-cookie', header);\n}\n\n/**\n * Set the token secret on the request.\n *\n * @param {IncomingMessage} req\n * @param {OutgoingMessage} res\n * @param {string} sessionKey\n * @param {string} val\n * @param {Object} [cookie]\n * @api private\n */\nfunction setSecret(\n  req: Request,\n  res: Response,\n  sessionKey: string,\n  val: string,\n  cookie?: CookieOptions,\n) {\n  if (cookie) {\n    // set secret on cookie\n    let value = val;\n\n    if (cookie.signed) {\n      // NOTE: This one is not expected to be hit, as if the cookie signature\n      // secret is not properly configured via \"cookie-parser\" middleware, that\n      // is checked and throws earlier in the code.\n      if (!req.secret) throw Error('Internal error');\n\n      value = `s:${sign(val, req.secret)}`;\n    }\n\n    setCookie(res, cookie.key, value, cookie);\n  } else {\n    // set secret on session\n    // TODO: Can we type it in a better way, to avoid such forced type-cast?\n    // eslint-disable-next-line no-param-reassign\n    (req as unknown as Record<string, { csrfSecret: string }>)[sessionKey]!\n      .csrfSecret = val;\n  }\n}\n\n/**\n * Verify the configuration against the request.\n * @private\n */\nfunction verifyConfiguration(\n  req: Request,\n  sessionKey: string,\n  cookie: CookieOptions | undefined,\n): boolean {\n  if (!getSecretBag(req, sessionKey, cookie)) {\n    return false;\n  }\n\n  // NOTE: `req.secret` is the cookie signature secret, configured and set by\n  // \"cookie-parser\" middleware: https://github.com/expressjs/cookie-parser\n  if (cookie && cookie.signed && !req.secret) {\n    return false;\n  }\n\n  return true;\n}\n\n/**\n * CSRF protection middleware.\n *\n * This middleware adds a `req.csrfToken()` function to make a token\n * which should be added to requests which mutate\n * state, within a hidden form field, query-string etc. This\n * token is validated against the visitor's session.\n *\n * @param {Object} options\n * @return {Function} middleware\n * @public\n */\nfunction csurf(options: Options = {}) {\n  // get cookie options\n  const cookie = getCookieOptions(options.cookie);\n\n  // get session options\n  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n  const sessionKey = options.sessionKey || 'session';\n\n  // get value getter\n  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n  const value = options.value || defaultValue;\n\n  // token repo\n  const tokens = new Tokens(options);\n\n  // ignored methods\n  const ignoreMethods = options.ignoreMethods ?? ['GET', 'HEAD', 'OPTIONS'];\n\n  if (!Array.isArray(ignoreMethods)) {\n    throw new TypeError('option ignoreMethods must be an array');\n  }\n\n  // generate lookup\n  const ignoreMethod = getIgnoredMethods(ignoreMethods);\n\n  return (req: Request, res: Response, next: NextFunction): void => {\n    // validate the configuration against request\n    if (!verifyConfiguration(req, sessionKey, cookie)) {\n      next(new Error('misconfigured csrf'));\n      return;\n    }\n\n    // get the secret from the request\n    let secret = getSecret(req, sessionKey, cookie);\n    let token: string;\n\n    // lazy-load token getter\n    // eslint-disable-next-line no-param-reassign\n    req.csrfToken = () => {\n      let sec = cookie ? secret : getSecret(req, sessionKey, cookie);\n\n      // use cached token if secret has not changed\n      if (token && sec === secret) {\n        return token;\n      }\n\n      // generate & set new secret\n      if (sec === undefined) {\n        sec = tokens.secretSync();\n        setSecret(req, res, sessionKey, sec, cookie);\n      }\n\n      // update changed secret\n      secret = sec;\n\n      // create new token\n      token = tokens.create(secret);\n\n      return token;\n    };\n\n    // generate & set secret\n    if (!secret) {\n      secret = tokens.secretSync();\n      setSecret(req, res, sessionKey, secret, cookie);\n    }\n\n    // verify the incoming token\n    if (\n      !ignoreMethod[req.method]\n      && !options.ignoreRequest?.(req)\n      && !verify(secret, value(req))\n    ) {\n      next(createError(403, 'invalid csrf token', {\n        code: 'EBADCSRFTOKEN',\n      }));\n      return;\n    }\n\n    next();\n  };\n}\n\nexport default csurf;\n"],"mappings":"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","ignoreList":[]}