UNPKG

n8n

Version:

n8n Workflow Automation Tool

141 lines 6.17 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpHeaderExtractor = void 0; const backend_common_1 = require("@n8n/backend-common"); const decorators_1 = require("@n8n/decorators"); const node_vm_1 = require("node:vm"); const zod_1 = require("zod"); const HttpHeaderExtractorOptionsSchema = zod_1.z.object({ headerName: zod_1.z.string().default('authorization'), headerValue: zod_1.z.string().default('[Bb][Ee][Aa][Rr][Ee][Rr]\\s+(.+)'), }); const MAX_HEADER_LENGTH = 8192; const REGEX_TIMEOUT_MS = 100; function isUnsafeRegexPattern(pattern) { const nestedQuantifier = /([+*?{]|\{\d+,?\d*\})\s*[)]\s*[+*?{]/; const overlappingAlt = /\([^)]*\|[^)]*\)[+*]/; return nestedQuantifier.test(pattern) || overlappingAlt.test(pattern); } function isHeaderObject(obj) { return obj !== null && obj !== undefined && typeof obj === 'object' && !Array.isArray(obj); } const regexContext = (0, node_vm_1.createContext)({ RegExp, pattern: '', input: '', result: null, }); const regexScript = new node_vm_1.Script('result = new RegExp(pattern).exec(input)'); function safeRegexExec(pattern, input, timeoutMs = REGEX_TIMEOUT_MS) { regexContext.pattern = pattern; regexContext.input = input; regexContext.result = null; try { regexScript.runInContext(regexContext, { timeout: timeoutMs }); return regexContext.result; } catch (error) { if (error.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') { return null; } throw error; } } let HttpHeaderExtractor = class HttpHeaderExtractor { constructor(logger) { this.logger = logger; this.hookDescription = { name: 'HttpHeaderExtractor', displayName: 'HTTP Header Extractor', options: [ { name: 'headerName', displayName: 'Header Name', type: 'string', default: 'authorization', description: 'The name of the HTTP header to extract the value from.', }, { name: 'headerValue', displayName: 'Header Value Pattern', type: 'string', default: '[Bb][Ee][Aa][Rr][Ee][Rr]\\s+(.+)', description: 'A regular expression pattern to extract the identity from the header value. Use a capturing group to specify the identity part.', }, ], }; } isApplicableToTriggerNode(nodeType) { return nodeType === 'n8n-nodes-base.webhook' || nodeType === 'webhook'; } async execute(options) { if (!options.triggerItems || options.triggerItems.length === 0) { this.logger.debug('No trigger items found, skipping HttpHeaderExtractor hook.'); return {}; } const httpHeaderOptions = await HttpHeaderExtractorOptionsSchema.safeParseAsync(options.options ?? {}); if (httpHeaderOptions.error) { this.logger.error('Invalid options for HttpHeaderExtractor hook.', { error: httpHeaderOptions.error, }); return {}; } const normalizedHeaderName = httpHeaderOptions.data.headerName.toLowerCase(); const pattern = httpHeaderOptions.data.headerValue; if (isUnsafeRegexPattern(pattern)) { this.logger.warn('Potentially unsafe regex pattern rejected', { pattern }); return {}; } const [triggerItem] = options.triggerItems; const headers = triggerItem.json['headers']; if (isHeaderObject(headers) && normalizedHeaderName in headers) { const headerValue = headers[normalizedHeaderName]; if (typeof headerValue === 'string') { headers[normalizedHeaderName] = '**********'; const truncatedValue = headerValue.slice(0, MAX_HEADER_LENGTH); try { const match = safeRegexExec(pattern, truncatedValue); if (match?.[1]) { return { triggerItems: options.triggerItems, contextUpdate: { credentials: { version: 1, identity: match[1], metadata: { source: 'http-header', headerName: normalizedHeaderName }, }, }, }; } else { return { triggerItems: options.triggerItems, }; } } catch (error) { this.logger.error('Invalid regex pattern', { pattern, error }); return { triggerItems: options.triggerItems, }; } } } return {}; } }; exports.HttpHeaderExtractor = HttpHeaderExtractor; exports.HttpHeaderExtractor = HttpHeaderExtractor = __decorate([ (0, decorators_1.ContextEstablishmentHook)(), __metadata("design:paramtypes", [backend_common_1.Logger]) ], HttpHeaderExtractor); //# sourceMappingURL=http-header-extractor.js.map