UNPKG

@bsv/auth-express-middleware

Version:
191 lines 7.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isLogLevelEnabled = isLogLevelEnabled; exports.getLogMethod = getLogMethod; exports.writeUrlToWriter = writeUrlToWriter; exports.writeRequestHeadersToWriter = writeRequestHeadersToWriter; exports.writeHeaderPair = writeHeaderPair; exports.writeBodyToWriter = writeBodyToWriter; exports.convertValueToArray = convertValueToArray; exports.makeDebugLogger = makeDebugLogger; const sdk_1 = require("@bsv/sdk"); const LOG_LEVELS = ['debug', 'info', 'warn', 'error']; /** * Helper to determine if a given message-level log should be output * based on the configured log level. */ function isLogLevelEnabled(configuredLevel, messageLevel) { return LOG_LEVELS.indexOf(messageLevel) >= LOG_LEVELS.indexOf(configuredLevel); } /** * Retrieves the appropriate logging method from the logger, * falling back to `log` if not found. * * Uses an explicit switch to avoid dynamic property access on a user-influenced * key, which prevents CodeQL js/unvalidated-dynamic-method-call alerts. */ function getLogMethod(logger, level) { switch (level) { case 'debug': return (typeof logger.debug === 'function' ? logger.debug : logger.log).bind(logger); case 'info': return (typeof logger.info === 'function' ? logger.info : logger.log).bind(logger); case 'warn': return (typeof logger.warn === 'function' ? logger.warn : logger.log).bind(logger); case 'error': return (typeof logger.error === 'function' ? logger.error : logger.log).bind(logger); default: return logger.log.bind(logger); } } /** * Write the URL pathname and search components to the binary writer. */ function writeUrlToWriter(parsedUrl, writer) { if (parsedUrl.pathname.length > 0) { const pathnameAsArray = sdk_1.Utils.toArray(parsedUrl.pathname); writer.writeVarIntNum(pathnameAsArray.length); writer.write(pathnameAsArray); } else { writer.writeVarIntNum(-1); } if (parsedUrl.search.length > 0) { const searchAsArray = sdk_1.Utils.toArray(parsedUrl.search); writer.writeVarIntNum(searchAsArray.length); writer.write(searchAsArray); } else { writer.writeVarIntNum(-1); } } /** * Collect and write signed request headers to the binary writer. */ function writeRequestHeadersToWriter(req, writer) { const includedHeaders = []; for (let [k, v] of Object.entries(req.headers)) { k = k.toLowerCase(); // Normalise to a single string — Express may return string[] when a header // is repeated (e.g. `Set-Cookie`). Take the first value to avoid // type-confusion (CodeQL js/type-confusion-through-parameter-tampering). const vStr = Array.isArray(v) ? v[0] : (typeof v === 'string' ? v : ''); let headerValue = vStr; if (k === 'content-type') { headerValue = vStr.split(';')[0].trim(); } if ((k.startsWith('x-bsv-') || k === 'content-type' || k === 'authorization') && !k.startsWith('x-bsv-auth')) { includedHeaders.push([k, headerValue]); } } includedHeaders.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); writer.writeVarIntNum(includedHeaders.length); for (const [headerKey, headerValue] of includedHeaders) { writeHeaderPair(writer, headerKey, headerValue); } } /** * Write a header pair (key + value) to the binary writer. */ function writeHeaderPair(writer, key, value) { const keyBytes = sdk_1.Utils.toArray(key, 'utf8'); writer.writeVarIntNum(keyBytes.length); writer.write(keyBytes); const valueBytes = sdk_1.Utils.toArray(value, 'utf8'); writer.writeVarIntNum(valueBytes.length); writer.write(valueBytes); } /** * Helper: Write body to writer */ function writeBodyToWriter(req, writer, logger, logLevel) { const { body, headers } = req; const debugLog = makeDebugLogger(logger, logLevel); // Inline-normalised content-type to a single string (Express may return string[]). // Inline narrowing rather than a helper so CodeQL's dataflow analysis can see // the explicit type guard (avoids js/type-confusion-through-parameter-tampering). const rawContentType = headers['content-type']; let contentType = ''; if (typeof rawContentType === 'string') { contentType = rawContentType; } else if (Array.isArray(rawContentType) && typeof rawContentType[0] === 'string') { contentType = rawContentType[0]; } if (Array.isArray(body) && body.every((item) => typeof item === 'number')) { writer.writeVarIntNum(body.length); writer.write(body); debugLog('[writeBodyToWriter] Body recognized as number[]', { length: body.length }); return; } if (body instanceof Uint8Array) { writer.writeVarIntNum(body.length); writer.write(Array.from(body)); debugLog('[writeBodyToWriter] Body recognized as Uint8Array', { length: body.length }); return; } if (contentType === 'application/json' && typeof body === 'object') { const bodyAsArray = sdk_1.Utils.toArray(JSON.stringify(body), 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); debugLog('[writeBodyToWriter] Body recognized as JSON', { body }); return; } if (contentType === 'application/x-www-form-urlencoded' && body !== null && typeof body === 'object' && !Array.isArray(body) && Object.keys(body).length > 0) { const parsedBody = new URLSearchParams(body).toString(); const bodyAsArray = sdk_1.Utils.toArray(parsedBody, 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); debugLog('[writeBodyToWriter] Body recognized as x-www-form-urlencoded', { parsedBody }); return; } if (contentType === 'text/plain' && typeof body === 'string' && body.length > 0) { const bodyAsArray = sdk_1.Utils.toArray(body, 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); debugLog('[writeBodyToWriter] Body recognized as text/plain', { body }); return; } // No valid body writer.writeVarIntNum(-1); debugLog('[writeBodyToWriter] No valid body to write', undefined); } /** * Helper: Convert values passed to res.send(...) into byte arrays */ function convertValueToArray(val, responseHeaders) { if (typeof val === 'string') { return sdk_1.Utils.toArray(val, 'utf8'); } if (val instanceof Buffer) { return Array.from(val); } if (typeof val === 'object' && val !== null) { if (!responseHeaders['content-type']) { responseHeaders['content-type'] = 'application/json'; } return sdk_1.Utils.toArray(JSON.stringify(val), 'utf8'); } if (typeof val === 'number') { return sdk_1.Utils.toArray(val.toString(), 'utf8'); } return sdk_1.Utils.toArray(String(val), 'utf8'); } /** * Returns a no-op or a bound debug logger depending on config. */ function makeDebugLogger(logger, logLevel) { if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { const fn = getLogMethod(logger, 'debug'); return (msg, data) => { if (data !== undefined) { fn(msg, data); } else { fn(msg); } }; } return () => { }; } //# sourceMappingURL=authMiddlewareHelpers.js.map