diff --git a/index.js b/index.js index 8de00e9..be9f625 100644 --- a/index.js +++ b/index.js @@ -8,5 +8,5 @@ */ export { ProxyHeadersAgent, ConnectError } from './lib/core/proxy-headers-agent.js'; -export { parseProxyUrl, parseTargetUrl, buildConnectRequest } from './lib/core/utils.js'; +export { parseProxyUrl, parseTargetUrl, buildConnectRequest, validateHeaderName, validateHeaderValue } from './lib/core/utils.js'; export { parseConnectResponse, hasCompleteHeaders } from './lib/core/connect-parser.js'; diff --git a/lib/core/utils.js b/lib/core/utils.js index 01e714b..fdadf1e 100644 --- a/lib/core/utils.js +++ b/lib/core/utils.js @@ -2,6 +2,40 @@ * Utility functions for proxy header handling. */ +const INVALID_HEADER_CHAR = /[\r\n\0]/; + +/** + * Validate that a header name does not contain characters that could + * enable CRLF injection in raw HTTP protocol strings. + * @param {string} name - Header name + * @throws {TypeError} If the name contains CR, LF, or NUL + */ +export function validateHeaderName(name) { + if (typeof name !== 'string' || name.length === 0) { + throw new TypeError('Header name must be a non-empty string'); + } + if (INVALID_HEADER_CHAR.test(name)) { + throw new TypeError( + `Invalid character in header name: ${JSON.stringify(name.slice(0, 50))}` + ); + } +} + +/** + * Validate that a header value does not contain characters that could + * enable CRLF injection in raw HTTP protocol strings. + * @param {string} value - Header value + * @throws {TypeError} If the value contains CR, LF, or NUL + */ +export function validateHeaderValue(value) { + const str = String(value); + if (INVALID_HEADER_CHAR.test(str)) { + throw new TypeError( + `Invalid character in header value: ${JSON.stringify(str.slice(0, 50))}` + ); + } +} + /** * Parse a proxy URL into components. * @param {string|URL} proxyUrl - The proxy URL @@ -60,6 +94,8 @@ export function buildConnectRequest(targetHost, targetPort, proxyAuth, proxyHead ? [...proxyHeaders.entries()] : Object.entries(proxyHeaders || {}); for (const [key, value] of entries) { + validateHeaderName(key); + validateHeaderValue(value); lines.push(`${key}: ${value}`); }