{"version":3,"file":"index.mjs","names":["create"],"sources":["../src/asyncMiddleware/asyncMiddleware.ts","../src/errorMiddleware/errorMiddleware.ts","../src/metricsMiddleware/metricsMiddleware.ts","../src/tracingHeaders/tracingHeaders.ts","../src/requestLogging/requestLogging.ts","../src/secureHeaders/secureHeaders.ts","../src/versionMiddleware/versionMiddleware.ts"],"sourcesContent":["import type { Middleware } from 'koa';\n\n/**\n * Wraps an asynchronously-initialised middleware to allow it to be\n * synchronously attached to a Koa application.\n *\n * This lazy loads the function supplied through the `init` parameter at time of\n * request. If initialisation fails, the error is thrown up the chain for\n * in-flight requests, and initialisation is retried on the next request.\n * If `ttl` is set, the middleware is re-initialised when cache expires.\n *\n * @param init  - Function to asynchronously initialise a middleware\n * @param ttl - Time in ms\n */\nexport const lazyLoad = <State, Context>(\n  init: () => Promise<Middleware<State, Context>>,\n  ttl?: number,\n): Middleware<State, Context> => {\n  let cache: Promise<Middleware<State, Context>> | undefined;\n  let cacheTimestamp: number;\n\n  const initOrInvalidate = async () => {\n    try {\n      const cacheInit = await init();\n      cacheTimestamp = Date.now();\n      return cacheInit;\n    } catch (err: unknown) {\n      cache = undefined;\n\n      throw err;\n    }\n  };\n\n  const validateCache = () =>\n    typeof cache === 'undefined' ||\n    typeof cacheTimestamp === 'undefined' ||\n    typeof ttl === 'undefined' ||\n    Date.now() - cacheTimestamp < ttl ||\n    (cache = undefined);\n\n  return async (ctx, next) => {\n    validateCache();\n    const middleware = await (cache ?? (cache = initOrInvalidate()));\n\n    return middleware(ctx, next) as unknown;\n  };\n};\n","import { isHttpError } from 'http-errors';\nimport type { Context, Middleware } from 'koa';\n\n/**\n * @see {@link https://github.com/microsoft/TypeScript/issues/1863}\n */\nconst ERROR_STATE_KEY = Symbol('seek-koala-error') as unknown as string;\n\nconst isObject = (value: unknown): value is Record<PropertyKey, unknown> =>\n  typeof value === 'object' && value !== null;\n\n/**\n * Custom error type supporting JSON response bodies\n *\n * The `handle` middleware will return either `message` or `body` depending on\n * the request's `Accept` header.\n *\n * ```javascript\n * ctx.throw(400, new JsonResponse('Invalid input', { fieldName: '/foo' }));\n * ```\n */\nexport class JsonResponse extends Error {\n  /**\n   * The property used by `handle` to infer that this error contains a body that\n   * can be exposed in the HTTP response.\n   */\n  public isJsonResponse = true as const;\n\n  /**\n   * Creates a new `JsonResponse`\n   *\n   * This must be passed to `ctx.throw` instead of being thrown directly.\n   *\n   * @param message - Plain text message used for requests preferring\n   *                  `text/plain`. This is also used as the `Error` superclass\n   *                  message.\n   *\n   * @param body - JavaScript value used for requests accepting\n   *               `application/json`. This is encoded as JSON in the response.\n   */\n  constructor(\n    message: string,\n    public body: unknown,\n  ) {\n    super(message);\n  }\n}\n\n/**\n * Catches errors thrown from downstream middleware, as specified here:\n *\n * https://github.com/koajs/koa/wiki/Error-Handling#catching-downstream-errors\n *\n * If you use `http-errors` or Koa's built-in `ctx.throw`, this tries to extract\n * a numeric error `status` to serve as the response status, and will set the\n * error message as the response body for non-5xx statuses.\n *\n * This includes support for a JSON response body by throwing an error with\n * `isJsonResponse` set to `true`. If the request accepts `application/json` the\n * error's `body` will be returned, otherwise its plain text `message`.\n *\n * This should be placed high up the middleware chain so that errors from lower\n * middleware are handled. It also serves to set the correct `ctx.status` for\n * middleware that emit logs or metrics containing the response status. For\n * this reason, we recommend a sequence like:\n *\n * ```javascript\n * app\n *   .use(requestLoggingMiddleware)\n *   .use(metricsMiddleware)\n *   .use(ErrorMiddleware.handle)\n *   .use(allTheRest);\n * ```\n */\nexport const handle: Middleware = async (ctx, next) => {\n  try {\n    return (await next()) as unknown;\n  } catch (err: unknown) {\n    ctx.state[ERROR_STATE_KEY] = err;\n\n    if (\n      !isObject(err) ||\n      typeof err.status !== 'number' ||\n      (!isHttpError(err) && err.isJsonResponse === undefined)\n    ) {\n      ctx.status = 500;\n      ctx.body = '';\n      return;\n    }\n\n    ctx.status = err.status;\n    const expose = err.status < 500;\n\n    if (\n      expose &&\n      err.body &&\n      err.isJsonResponse === true &&\n      // Prefer JSON ourselves if the request has no preference\n      ctx.accepts(['application/json', 'text/plain']) === 'application/json'\n    ) {\n      ctx.body = err.body;\n      return;\n    }\n\n    ctx.body = (expose && err.message) || '';\n    return;\n  }\n};\n\n/**\n * Retrieve the error caught by `ErrorMiddleware.handle` from state.\n *\n * This is useful if you want to do something with the error higher up in the\n * middleware chain.\n */\nexport const thrown = (ctx: Context): unknown =>\n  ctx.state[ERROR_STATE_KEY] as unknown;\n","import type Koa from 'koa';\n\nimport type { StatsD } from './statsD.js';\n\n/**\n * Returns metrics tags for the passed Koa context\n *\n * Typically at least the route name should be returned. Due to the various\n * ways to perform routing in Koa there is no universal way to accomplish this.\n *\n * This is called during the response chain. This gives it access to the full\n * request and response.\n */\ntype TagsForContext = (ctx: Koa.Context) => Record<string, unknown> | undefined;\n\n/**\n * Creates a new request metrics middleware\n *\n * This records a `request.distribution` metric for every request. It will have\n * `http_status` and `http_status_family` tags in addition to what's returned\n * by `tagsForContext`.\n *\n * If `skipRequestLogging` is set on the state the request will not be logged.\n *\n * This should be attached early in the middleware chain to ensure metrics can\n * be recorded for requests rejected by downstream middleware and the latency\n * is inclusive.\n *\n * @param metricsClient  - Metrics client to use for recording metrics\n * @param tagsForContext - Function to return a base set of metrics tags\n */\nexport const create = (\n  metricsClient: StatsD,\n  tagsForContext: TagsForContext,\n  sampleRate: number = 1,\n): Koa.Middleware =>\n  async function metricsMiddleware(\n    ctx: Koa.Context,\n    next: () => Promise<unknown>,\n  ): Promise<void> {\n    const startTime = process.hrtime.bigint();\n    let status = 500;\n\n    try {\n      await next();\n      status = ctx.status;\n    } finally {\n      const tags = {\n        http_status: `${status}`,\n        http_status_family: `${Math.floor(status / 100)}xx`,\n        http_method: ctx.method.toLowerCase(),\n        ...tagsForContext(ctx),\n      };\n\n      const durationNanos = process.hrtime.bigint() - startTime;\n\n      if (!ctx.state.skipRequestLogging) {\n        metricsClient.distribution(\n          'request.distribution',\n          Number(durationNanos) / 1e6,\n          sampleRate,\n          tags,\n        );\n      }\n    }\n  };\n","import { randomUUID } from 'crypto';\n\nimport type Koa from 'koa';\n\nimport type { AppIdentifier } from '../types.js';\n\nconst STATE_KEY = '__seek_koala_tracing';\n\n/** HTTP header name for the request ID */\nexport const REQUEST_ID_HEADER = 'x-request-id';\n\n/** HTTP header name for the event capture session ID */\nexport const EC_SESSION_ID_HEADER = 'x-seek-ec-sessionid';\n\n/** HTTP header name for the event capture visitor ID */\nexport const EC_VISITOR_ID_HEADER = 'x-seek-ec-visitorid';\n\n/** HTTP header name for an ad-hoc session ID */\nexport const ADHOC_SESSION_ID_HEADER = 'x-session-id';\n\n/** Encapsulates the extracted tracing from an incoming request */\nexport interface SEEKTracing {\n  /**\n   * Unique identifier for a client-initiated request\n   *\n   * The semantics of this header is documented in SEEK RFC002.\n   */\n  requestID: string;\n\n  /**\n   * Optional event capture session ID\n   *\n   * Reflects the value of the candidate's `JobseekerSessionId` cookie.\n   */\n  ecSessionID?: string;\n\n  /**\n   * Optional event capture visitor ID\n   *\n   * Reflects the value of the candidate's `JobseekerVisitorId` cookie.\n   */\n  ecVisitorID?: string;\n\n  /**\n   * Optional ad-hoc session ID\n   *\n   * Defined contextually by the system using it.\n   */\n  adhocSessionID?: string;\n}\n\nconst SESSION_HEADER_TO_TRACING_PROP = [\n  [EC_SESSION_ID_HEADER, 'ecSessionID'],\n  [EC_VISITOR_ID_HEADER, 'ecVisitorID'],\n  [ADHOC_SESSION_ID_HEADER, 'adhocSessionID'],\n] as const;\n\nconst generateTracing = (ctx: Koa.Context): SEEKTracing => {\n  const header = ctx.request.header as Record<string, string>;\n\n  const tracing: SEEKTracing = {\n    requestID: header[REQUEST_ID_HEADER] || randomUUID(),\n  };\n\n  for (const [headerName, tracingProp] of SESSION_HEADER_TO_TRACING_PROP) {\n    const headerValue = header[headerName];\n    if (headerValue) {\n      tracing[tracingProp] = headerValue;\n    }\n  }\n\n  return tracing;\n};\n\n/**\n * Extracts tracing information from an incoming request\n *\n * If no request ID is present one will be generated and cached on the state.\n */\nexport const tracingFromContext = (ctx: Koa.Context): SEEKTracing => {\n  const state = ctx.state as { [STATE_KEY]?: SEEKTracing };\n\n  // We need to cache this in case we generate a new request ID\n  const cachedTracing = state[STATE_KEY];\n  if (typeof cachedTracing !== 'undefined') {\n    return cachedTracing;\n  }\n\n  const newTracing = generateTracing(ctx);\n  state[STATE_KEY] = newTracing;\n  return newTracing;\n};\n\n/**\n * Generates tracing headers for an outgoing request\n *\n * This sets both the SEEK tracing headers and a distinctive `User-Agent`.\n */\nexport const outgoingHeaders = (\n  appID: AppIdentifier,\n  tracing: SEEKTracing,\n): Record<string, string> => {\n  const userAgent = appID.version\n    ? `${appID.name}/${appID.version}`\n    : `${appID.name}`;\n\n  const headers: Record<string, string> = {\n    'user-agent': userAgent,\n    [REQUEST_ID_HEADER]: tracing.requestID,\n  };\n\n  for (const [headerName, tracingProp] of SESSION_HEADER_TO_TRACING_PROP) {\n    const headerValue = tracing[tracingProp];\n    if (headerValue) {\n      headers[headerName] = headerValue;\n    }\n  }\n\n  return headers;\n};\n","import { AsyncLocalStorage } from 'async_hooks';\n\nimport type Koa from 'koa';\n\nimport { thrown } from '../errorMiddleware/errorMiddleware.js';\nimport { tracingFromContext } from '../tracingHeaders/tracingHeaders.js';\n\n/**\n * Key-value pairs of fields to log\n */\nexport type Fields = Record<string, unknown>;\n\n/**\n * Defines a set of substitutions to perform on request headers\n *\n * This is typically used to mask sensitive headers. However, it can also be\n * used to omit uninteresting headers from the request log by replacing them\n * with `undefined`.\n */\ntype HeaderReplacements = Record<string, string | undefined>;\n\n// The Koala emoji should hopefully be a hint that:\n// 1. This isn't an actual value of a header\n// 2. Koala is doing the redaction\nconst REDACTED_HEADER = '🐨 REDACTED 🙅';\n\n/**\n * Header substitutions for masking sensitive data\n *\n * These headers typically contain user credentials such as JWTs or session\n * cookies.\n */\nexport const SENSITIVE_HEADER_REPLACEMENTS: HeaderReplacements = {\n  'authenticated-user': REDACTED_HEADER,\n  authorization: REDACTED_HEADER,\n  cookie: REDACTED_HEADER,\n  'user-email': REDACTED_HEADER,\n  'x-forwarded-id-token': REDACTED_HEADER,\n  'x-seek-oidc-identity': REDACTED_HEADER,\n};\n\n/**\n * Koa context state extensions for request logging\n */\nexport interface State {\n  /**\n   * Indicates a request shouldn't appear in the request log\n   */\n  skipRequestLogging?: boolean;\n}\n\nconst replaceHeaders = (\n  headers: Record<string, unknown>,\n  replacements: HeaderReplacements,\n): Record<string, unknown> => {\n  const cleanedHeaders = {\n    ...headers,\n  };\n\n  for (const headerName of Object.keys(cleanedHeaders)) {\n    const normalisedHeaderName = headerName.toLowerCase();\n\n    if (replacements.hasOwnProperty(normalisedHeaderName)) {\n      cleanedHeaders[headerName] = replacements[normalisedHeaderName];\n    }\n  }\n\n  return cleanedHeaders;\n};\n\n/**\n * Returns context fields for the passed Koa context\n */\nexport type ContextFields = (\n  ctx: Koa.Context,\n  fields?: Fields,\n) => Record<string, unknown>;\n\n/**\n * Returns an object of request-specific log fields\n *\n * The returned object includes key-value pairs for the request method, route,\n * URL and tracing IDs. This can be used to construct a child logger that\n * annotates log entries with request-specific information.\n *\n * The route properties assume use of `@koa/router`, and are omitted if the\n * expected metadata is not present on context.\n *\n * @param ctx - Koa Context\n * @param fields - Optional fields to add to the object\n */\nexport const contextFields: ContextFields = (ctx, fields): Fields => {\n  const { adhocSessionID, requestID } = tracingFromContext(ctx);\n\n  return {\n    method: ctx.request.method,\n    ...(typeof ctx._matchedRoute === 'string' && {\n      route: ctx._matchedRoute,\n    }),\n    ...(typeof ctx._matchedRouteName === 'string' && {\n      routeName: ctx._matchedRouteName,\n    }),\n    url: ctx.request.url,\n    'x-request-id': requestID,\n    ...(typeof adhocSessionID === 'string' && {\n      'x-session-id': adhocSessionID,\n    }),\n    ...fields,\n  };\n};\n\n/**\n * Creates middleware for logging requests and their responses\n *\n * This calls `logFn` for every response with a set of fields to be logged.\n * This will typically call the app's logger with a fixed message.\n *\n * In addition to the fields returned by `contextFields` this adds the\n * request headers, response latency, final status code. In the case of\n * uncaught exceptions it will also add the error string.\n *\n * If `skipRequestLogging` is set on the state the request will not be logged.\n *\n * This should be attached early in the request chain to ensure log entries can\n * be created for requests rejected by downstream middleware and the recorded\n * latency is inclusive.\n */\nexport const createMiddleware = <StateT extends State, CustomT>(\n  logFn: (\n    ctx: Koa.ParameterizedContext<StateT, CustomT>,\n    fields: Fields,\n    error?: unknown,\n  ) => void,\n  headerReplacements: HeaderReplacements = SENSITIVE_HEADER_REPLACEMENTS,\n): Koa.Middleware<StateT, CustomT> =>\n  async function requestLogMiddleware(\n    ctx: Koa.ParameterizedContext<StateT, CustomT>,\n    next: () => Promise<unknown>,\n  ): Promise<void> {\n    const startTime = Date.now();\n\n    const requestFinished = (\n      resultFields: Record<string, unknown>,\n      err?: unknown,\n    ) => {\n      if (ctx.state.skipRequestLogging) {\n        return;\n      }\n\n      const latency = Date.now() - startTime;\n      logFn(\n        ctx,\n        {\n          ...(typeof err !== 'undefined' && {\n            err,\n            // eslint-disable-next-line @typescript-eslint/no-base-to-string\n            internalErrorString: String(err),\n          }),\n          latency,\n          headers: replaceHeaders(ctx.request.header, headerReplacements),\n          ...contextFields(ctx),\n          ...resultFields,\n        },\n        err,\n      );\n    };\n\n    try {\n      await next();\n\n      requestFinished({ status: ctx.response.status }, thrown(ctx));\n    } catch (err: unknown) {\n      requestFinished({ status: 500 }, err);\n\n      throw err;\n    }\n  };\n\n/*\n * Creates a logger context storage instance\n *\n */\nexport const createContextStorage = () => {\n  const loggerContext = new AsyncLocalStorage<Fields>();\n\n  return {\n    /**\n     * Koa Middleware that injects the logger context into an AsyncLocalStorage instance\n     * @param getFieldsFn - Optional function to return a set of fields to include in context. Defaults to `contextFields`\n     */\n    createContextMiddleware:\n      (getFieldsFn: ContextFields = contextFields): Koa.Middleware =>\n      async (ctx, next) => {\n        await loggerContext.run(getFieldsFn(ctx, contextFields(ctx)), next);\n      },\n    /**\n     * Returns a shallow copy of fields from the logger context store. For performance reason we only copy the surface level fields.\n     */\n    mixin: () => ({ ...loggerContext.getStore() }),\n  };\n};\n","import type Koa from 'koa';\n\n/**\n * Adds HTTP response headers that opt-in to stricter browser security policies\n *\n * This middleware makes the following assumptions:\n * 1. The app's domain does not mix HTTP and HTTPS\n * 2. The response is an API response, i.e. it is not being directly rendered\n *    by the browser\n * 3. The responses have an accurate `Content-Type` header\n */\nexport const middleware: Koa.Middleware = async (ctx, next) => {\n  // WARNING: Any semantic changes to these headers should be treated as a\n  // breaking change of Koala.\n\n  // Enforce HTTPS when connecting to this domain for a year.\n  // This works with local testing over HTTP - the header is ignored when\n  // received over HTTP;\n  ctx.set('Strict-Transport-Security', 'max-age=31536000');\n\n  // Disable HTML from loading resources from any source.\n  // This prevents the browser from rendering any complex HTML beyond e.g.\n  // trivial error pages.\n  ctx.set('Content-Security-Policy', \"default-src 'none'\");\n\n  // Do not render this content if a reflected XSS attack is detected.\n  // This is mostly redundant as our `Content-Security-Policy` does not allow\n  // inline scripts.\n  ctx.set('X-XSS-Protection', '1; mode=block');\n\n  // Prevent us from being framed or embedded.\n  ctx.set('X-Frame-Options', 'deny');\n\n  // Disable MIME type sniffing.\n  // This prevents attacks where the attacker can manipulate the response in to\n  // being detected the wrong MIME type.\n  ctx.set('X-Content-Type-Options', 'nosniff');\n\n  await next();\n};\n","import type Koa from 'koa';\n\nimport type { AppIdentifier } from '../types.js';\n\n/**\n * Creates a middleware for attaching app version information to responses\n *\n * This has no dependencies and can be added in any order.\n */\nexport const create = (appID: AppIdentifier): Koa.Middleware =>\n  async function versionMiddleware<T>(\n    ctx: Koa.Context,\n    next: () => Promise<T>,\n  ): Promise<T> {\n    ctx.set(\n      'Server',\n      appID.version ? `${appID.name}/${appID.version}` : appID.name,\n    );\n\n    if (appID.version) {\n      ctx.set('X-Api-Version', appID.version);\n    }\n\n    return next();\n  };\n"],"mappings":"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"}