UNPKG

@angular/ssr

Version:

Angular server side rendering utilities

1 lines 20.3 kB
{"version":3,"file":"_validation-chunk.mjs","sources":["../../../../../../k8-fastbuild-ST-fdfa778d11ba/bin/packages/angular/ssr/src/utils/validation.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Internal sentinel string representing a wildcard rule to trust all proxy headers.\n */\nconst TRUST_ALL_PROXY_HEADERS = '*';\n\n/**\n * The set of headers that should be validated for host header injection attacks.\n */\nconst HOST_HEADERS_TO_VALIDATE: ReadonlyArray<string> = ['host', 'x-forwarded-host'];\n\n/**\n * Regular expression to validate that the port is a numeric value.\n */\nconst VALID_PORT_REGEX = /^\\d+$/;\n\n/**\n * Regular expression to validate that the protocol is either http or https (case-insensitive).\n */\nconst VALID_PROTO_REGEX = /^https?$/i;\n\n/**\n * Regular expression to validate that the prefix is valid. Validates that the prefix is a valid path prefix and nothing else.\n * It validates that the prefix starts with a forward slash and contains only letters, numbers, hyphens, underscores and forward slashes.\n */\nconst VALID_PREFIX_REGEX = /^\\/([a-z0-9_-]+\\/)*[a-z0-9_-]*$/i;\n\n/**\n * Extracts the first value from a multi-value header string.\n *\n * @param value - A string or an array of strings representing the header values.\n * If it's a string, values are expected to be comma-separated.\n * @returns The first trimmed value from the multi-value header, or `undefined` if the input is invalid or empty.\n *\n * @example\n * ```typescript\n * getFirstHeaderValue(\"value1, value2, value3\"); // \"value1\"\n * getFirstHeaderValue([\"value1\", \"value2\"]); // \"value1\"\n * getFirstHeaderValue(undefined); // undefined\n * ```\n */\nexport function getFirstHeaderValue(\n value: string | string[] | undefined | null,\n): string | undefined {\n return value?.toString().split(',', 1)[0]?.trim();\n}\n\n/**\n * Validates a request.\n *\n * @param request - The incoming `Request` object to validate.\n * @param allowedHosts - A set of allowed hostnames.\n * @param disableHostCheck - Whether to disable the host check.\n * @throws Error if any of the validated headers contain invalid values.\n */\nexport function validateRequest(\n request: Request,\n allowedHosts: ReadonlySet<string>,\n disableHostCheck: boolean,\n): void {\n validateHeaders(request, allowedHosts, disableHostCheck);\n\n if (!disableHostCheck) {\n validateUrl(new URL(request.url), allowedHosts);\n }\n}\n\n/**\n * Validates that the hostname of a given URL is allowed.\n *\n * @param url - The URL object to validate.\n * @param allowedHosts - A set of allowed hostnames.\n * @throws Error if the hostname is not in the allowlist.\n */\nexport function validateUrl(url: URL, allowedHosts: ReadonlySet<string>): void {\n const { hostname } = url;\n if (!isHostAllowed(hostname, allowedHosts)) {\n throw new Error(`URL with hostname \"${hostname}\" is not allowed.`);\n }\n}\n\n/**\n * Sanitizes the proxy headers of a request by removing unallowed `X-Forwarded-*` headers.\n * If no headers need to be removed, the original request is returned without cloning.\n *\n * @param request - The incoming `Request` object to sanitize.\n * @param trustProxyHeaders - A set of allowed proxy headers.\n * @returns The sanitized request, or the original request if no changes were needed.\n */\nexport function sanitizeRequestHeaders(\n request: Request,\n trustProxyHeaders: ReadonlySet<string>,\n): Request {\n let headersDeleted = false;\n const headers = new Headers();\n\n for (const [key, value] of request.headers) {\n const lowerKey = key.toLowerCase();\n const isProxyHeader = lowerKey === 'forwarded' || lowerKey.startsWith('x-forwarded-');\n if (isProxyHeader && !isProxyHeaderAllowed(lowerKey, trustProxyHeaders)) {\n // eslint-disable-next-line no-console\n console.warn(\n `Received \"${key}\" header but \"trustProxyHeaders\" was not set up to allow it.\\n` +\n `For more information, see https://angular.dev/best-practices/security#configuring-trusted-proxy-headers`,\n );\n headersDeleted = true;\n } else {\n headers.set(key, value);\n }\n }\n\n return headersDeleted\n ? new Request(request.clone(), {\n signal: request.signal,\n headers,\n })\n : request;\n}\n\n/**\n * Validates a specific host header value against the allowed hosts.\n *\n * @param headerName - The name of the header to validate (e.g., 'host', 'x-forwarded-host').\n * @param headerValue - The value of the header to validate.\n * @param allowedHosts - A set of allowed hostnames.\n * @throws Error if the header value is invalid or the hostname is not in the allowlist.\n */\nfunction verifyHostAllowed(\n headerName: string,\n headerValue: string,\n allowedHosts: ReadonlySet<string>,\n): void {\n const url = `http://${headerValue}`;\n if (!URL.canParse(url)) {\n throw new Error(`Header \"${headerName}\" contains an invalid value and cannot be parsed.`);\n }\n\n const { hostname, pathname, search, hash, username, password } = new URL(url);\n if (pathname !== '/' || search || hash || username || password) {\n throw new Error(\n `Header \"${headerName}\" with value \"${headerValue}\" contains characters that are not allowed.`,\n );\n }\n\n if (!isHostAllowed(hostname, allowedHosts)) {\n throw new Error(`Header \"${headerName}\" with value \"${headerValue}\" is not allowed.`);\n }\n}\n\n/**\n * Checks if the hostname is allowed.\n * @param hostname - The hostname to check.\n * @param allowedHosts - A set of allowed hostnames.\n * @returns `true` if the hostname is allowed, `false` otherwise.\n */\nfunction isHostAllowed(hostname: string, allowedHosts: ReadonlySet<string>): boolean {\n if (allowedHosts.has('*') || allowedHosts.has(hostname)) {\n return true;\n }\n\n for (const allowedHost of allowedHosts) {\n if (!allowedHost.startsWith('*.')) {\n continue;\n }\n\n const domain = allowedHost.slice(1);\n if (hostname.endsWith(domain)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Validates the headers of an incoming request.\n *\n * @param request - The incoming `Request` object containing the headers to validate.\n * @param allowedHosts - A set of allowed hostnames.\n * @param disableHostCheck - Whether to disable the host check.\n * @throws Error if any of the validated headers contain invalid values.\n */\nfunction validateHeaders(\n request: Request,\n allowedHosts: ReadonlySet<string>,\n disableHostCheck: boolean,\n): void {\n const headers = request.headers;\n for (const headerName of HOST_HEADERS_TO_VALIDATE) {\n const headerValue = getFirstHeaderValue(headers.get(headerName));\n if (headerValue && !disableHostCheck) {\n verifyHostAllowed(headerName, headerValue, allowedHosts);\n }\n }\n\n const forwarded = headers.get('forwarded');\n if (forwarded) {\n const forwardedParams = parseForwardedHeader(forwarded);\n if (forwardedParams.host && !disableHostCheck) {\n verifyHostAllowed('Forwarded \"host\"', forwardedParams.host, allowedHosts);\n }\n if (forwardedParams.proto && !VALID_PROTO_REGEX.test(forwardedParams.proto)) {\n throw new Error('Header \"forwarded\" proto parameter must be either \"http\" or \"https\".');\n }\n }\n\n const xForwardedPort = getFirstHeaderValue(headers.get('x-forwarded-port'));\n if (xForwardedPort && !VALID_PORT_REGEX.test(xForwardedPort)) {\n throw new Error('Header \"x-forwarded-port\" must be a numeric value.');\n }\n\n const xForwardedProto = getFirstHeaderValue(headers.get('x-forwarded-proto'));\n if (xForwardedProto && !VALID_PROTO_REGEX.test(xForwardedProto)) {\n throw new Error('Header \"x-forwarded-proto\" must be either \"http\" or \"https\".');\n }\n\n const xForwardedPrefix = getFirstHeaderValue(headers.get('x-forwarded-prefix'));\n if (xForwardedPrefix && !VALID_PREFIX_REGEX.test(xForwardedPrefix)) {\n throw new Error(\n 'Header \"x-forwarded-prefix\" is invalid. It must start with a \"/\" and contain ' +\n 'only alphanumeric characters, hyphens, and underscores, separated by single slashes.',\n );\n }\n}\n\n/**\n * Checks if a specific proxy header is allowed.\n *\n * @param headerName - The name of the proxy header to check.\n * @param trustProxyHeaders - A set of allowed proxy headers.\n * @returns `true` if the header is allowed, `false` otherwise.\n */\nexport function isProxyHeaderAllowed(\n headerName: string,\n trustProxyHeaders: ReadonlySet<string>,\n): boolean {\n return (\n trustProxyHeaders.has(TRUST_ALL_PROXY_HEADERS) ||\n trustProxyHeaders.has(headerName.toLowerCase())\n );\n}\n\n/**\n * Normalizes the `trustProxyHeaders` option to a consistent representation.\n * @param trustProxyHeaders The input `trustProxyHeaders` value.\n * @returns A `Set<string>` of normalized header names.\n */\nexport function normalizeTrustProxyHeaders(\n trustProxyHeaders: boolean | readonly string[] | undefined,\n): ReadonlySet<string> {\n if (!trustProxyHeaders) {\n return new Set();\n }\n\n if (trustProxyHeaders === true) {\n return new Set([TRUST_ALL_PROXY_HEADERS]);\n }\n\n const normalizedTrustedProxyHeaders = new Set<string>();\n for (const header of trustProxyHeaders) {\n const lowerHeader = header.toLowerCase();\n if (lowerHeader === TRUST_ALL_PROXY_HEADERS) {\n throw new Error(\n `\"${TRUST_ALL_PROXY_HEADERS}\" is not allowed as a value for the \"trustProxyHeaders\" option.`,\n );\n }\n const isValid = lowerHeader === 'forwarded' || lowerHeader.startsWith('x-forwarded-');\n if (!isValid) {\n throw new Error(\n `\"${header}\" is not a valid proxy header. Trusted proxy headers must be \"forwarded\" or start with \"x-forwarded-\".`,\n );\n }\n normalizedTrustedProxyHeaders.add(lowerHeader);\n }\n\n return normalizedTrustedProxyHeaders;\n}\n\n/**\n * Parses the standard `Forwarded` header (RFC 7239).\n * It extracts the parameters from the first (leftmost) element in the header.\n *\n * @param headerValue - The value of the `Forwarded` header.\n * @returns A record of lowercase parameter names to their values.\n */\nexport function parseForwardedHeader(\n headerValue: string | null | undefined,\n): Record<string, string> {\n if (!headerValue) {\n return {};\n }\n\n const params: Record<string, string> = {};\n let inQuotes = false;\n let escaped = false;\n let currentKey = '';\n let currentValue = '';\n let isParsingValue = false;\n let isKeyEnded = false;\n let isParsingValueEnded = false;\n\n for (const char of headerValue) {\n if (escaped) {\n escaped = false;\n if (isParsingValue) {\n currentValue += char;\n } else {\n currentKey += char;\n }\n continue;\n }\n\n if (char === '\\\\') {\n if (inQuotes) {\n escaped = true;\n } else if (isParsingValue) {\n currentValue += char;\n } else {\n currentKey += char;\n }\n continue;\n }\n\n if (char === '\"') {\n inQuotes = !inQuotes;\n continue;\n }\n\n if (inQuotes) {\n if (isParsingValue) {\n currentValue += char;\n } else {\n currentKey += char;\n }\n continue;\n }\n\n if (char === ',') {\n addParam(currentKey, currentValue, isParsingValue, params);\n break;\n }\n\n if (char === ';') {\n addParam(currentKey, currentValue, isParsingValue, params);\n currentKey = '';\n currentValue = '';\n isParsingValue = false;\n isKeyEnded = false;\n isParsingValueEnded = false;\n continue;\n }\n\n if (char === '=') {\n if (!isParsingValue) {\n isParsingValue = true;\n } else {\n currentValue += char;\n }\n continue;\n }\n\n if (char === ' ' || char === '\\t') {\n if (isParsingValue) {\n if (currentValue.length > 0) {\n isParsingValueEnded = true;\n }\n } else if (currentKey.length > 0) {\n isKeyEnded = true;\n }\n continue;\n }\n\n if (isParsingValue) {\n if (!isParsingValueEnded) {\n currentValue += char;\n }\n } else if (isKeyEnded) {\n currentKey = char;\n isKeyEnded = false;\n } else {\n currentKey += char;\n }\n }\n\n if (currentKey || currentValue || isParsingValue) {\n addParam(currentKey, currentValue, isParsingValue, params);\n }\n\n return params;\n}\n\n/**\n * Helper function to add a parameter to the params object.\n * @param key - The key to add.\n * @param value - The value to add.\n * @param hasValue - Whether the parameter has a value.\n * @param params - The params object to add the parameter to.\n */\nfunction addParam(\n key: string,\n value: string,\n hasValue: boolean,\n params: Record<string, string>,\n): void {\n if (!hasValue) {\n return;\n }\n\n const trimmedKey = key.trim().toLowerCase();\n if (trimmedKey) {\n params[trimmedKey] = value;\n }\n}\n"],"names":[],"mappings":"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"}