From 64859108483e867c37e57f20ea2892c6e813ec7f Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Thu, 27 Aug 2026 08:20:59 +0200 Subject: [PATCH 1/7] fix(appsec): send the normalized status code on end-invocation --- src/appsec/index.spec.ts | 36 ++++++++++++++ src/appsec/index.ts | 10 +++- src/trace/listener.spec.ts | 98 +++++++++++++++++++++++++++++++++++++- src/trace/listener.ts | 28 ++++++----- 4 files changed, 157 insertions(+), 15 deletions(-) diff --git a/src/appsec/index.spec.ts b/src/appsec/index.spec.ts index 51b72316..d44795c7 100644 --- a/src/appsec/index.spec.ts +++ b/src/appsec/index.spec.ts @@ -101,5 +101,41 @@ describe("AppSec orchestrator", () => { responseHeaders: undefined, }); }); + + it("should prefer the normalized status code over the raw one from the result", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 200 }, "502"); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "502", + responseHeaders: undefined, + }); + }); + + it("should publish the normalized status code when the result carries none", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { headers: { "content-type": "application/json" } }, "200"); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: { "content-type": "application/json" }, + }); + }); + + it("should fall back to the raw status code when no normalized one is given", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 204 }, undefined); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "204", + responseHeaders: undefined, + }); + }); }); }); diff --git a/src/appsec/index.ts b/src/appsec/index.ts index ee3d8687..2aa4bbd1 100644 --- a/src/appsec/index.ts +++ b/src/appsec/index.ts @@ -30,12 +30,18 @@ export function processAppsecRequest(event: any, span: any): void { }); } -export function processAppsecResponse(span: any, result: any): void { +/** + * @param span + * @param result + * @param statusCode Status code already normalized by the trigger layer. Falls back to the raw + * `result.statusCode` when the caller has none, which happens for non-HTTP triggers. + */ +export function processAppsecResponse(span: any, result: any, statusCode?: string): void { if (!span || !endInvocationChannel.hasSubscribers) return; endInvocationChannel.publish({ span, - statusCode: result?.statusCode?.toString(), + statusCode: statusCode ?? result?.statusCode?.toString(), responseHeaders: result?.headers as Record | undefined, }); } diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index 5e1399dd..9cede1f8 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -1,3 +1,4 @@ +import { readFileSync } from "fs"; import { TraceListener } from "./listener"; import { ddtraceVersion, parentSpanFinishTimeHeader } from "./constants"; import { datadogLambdaVersion } from "../constants"; @@ -698,7 +699,102 @@ describe("TraceListener", () => { listener.onEndingInvocation(event, result, false); expect(mockProcessAppsecResponse).toHaveBeenCalledTimes(1); - expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, result); + // Non-HTTP trigger: there is no normalized status code to hand over. + expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, result, undefined); + } finally { + currentSpanSpy.mockRestore(); + } + }); + + it("passes the normalized status code instead of the raw one for API Gateway v2", async () => { + const mockSetTag = jest.fn(); + const mockSpan = { setTag: mockSetTag }; + const currentSpanSpy = jest.spyOn(TracerWrapper.prototype, "currentSpan", "get").mockReturnValue(mockSpan); + + try { + const listener = new TraceListener(defaultConfig); + const event = JSON.parse(readFileSync("./event_samples/api-gateway-v2.json", "utf8")); + // API Gateway v2 defaults to 200 when the handler omits the status code, so the raw + // result value (undefined) is not what AppSec should see. + const result = { body: "ok" }; + await listener.onStartInvocation(event, context as any); + listener.onEndingInvocation(event, result, false); + + expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, result, "200"); + } finally { + currentSpanSpy.mockRestore(); + } + }); + + it("passes the normalized 502 when a buffered function returned no result", async () => { + const mockSetTag = jest.fn(); + const mockSpan = { setTag: mockSetTag }; + const currentSpanSpy = jest.spyOn(TracerWrapper.prototype, "currentSpan", "get").mockReturnValue(mockSpan); + + try { + const listener = new TraceListener(defaultConfig); + const event = JSON.parse(readFileSync("./event_samples/application-load-balancer.json", "utf8")); + await listener.onStartInvocation(event, context as any); + listener.onEndingInvocation(event, undefined, false); + + expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, undefined, "502"); + } finally { + currentSpanSpy.mockRestore(); + } + }); + + it("passes the normalized 200 when a streaming function returned no result", async () => { + const mockSetTag = jest.fn(); + const mockSpan = { setTag: mockSetTag }; + const currentSpanSpy = jest.spyOn(TracerWrapper.prototype, "currentSpan", "get").mockReturnValue(mockSpan); + + try { + const listener = new TraceListener(defaultConfig); + const event = JSON.parse(readFileSync("./event_samples/api-gateway-v2.json", "utf8")); + await listener.onStartInvocation(event, context as any); + listener.onEndingInvocation(event, undefined, true); + + expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, undefined, "200"); + } finally { + currentSpanSpy.mockRestore(); + } + }); + + it("tags http.status_code on the span before calling processAppsecResponse", async () => { + const callOrder: string[] = []; + mockProcessAppsecResponse.mockImplementation(() => callOrder.push("appsec")); + + const mockSetTag = jest.fn((key: string) => { + if (key === "http.status_code") callOrder.push("tag"); + }); + const mockSpan = { setTag: mockSetTag }; + const currentSpanSpy = jest.spyOn(TracerWrapper.prototype, "currentSpan", "get").mockReturnValue(mockSpan); + + try { + const listener = new TraceListener(defaultConfig); + const event = JSON.parse(readFileSync("./event_samples/api-gateway-v2.json", "utf8")); + await listener.onStartInvocation(event, context as any); + listener.onEndingInvocation(event, { statusCode: 201 }, false); + + expect(callOrder).toEqual(["tag", "appsec"]); + } finally { + currentSpanSpy.mockRestore(); + } + }); + + it("still calls processAppsecResponse on a 5xx response that short-circuits onEndingInvocation", async () => { + const mockSetTag = jest.fn(); + const mockSpan = { setTag: mockSetTag }; + const currentSpanSpy = jest.spyOn(TracerWrapper.prototype, "currentSpan", "get").mockReturnValue(mockSpan); + + try { + const listener = new TraceListener(defaultConfig); + const event = JSON.parse(readFileSync("./event_samples/api-gateway-v2.json", "utf8")); + await listener.onStartInvocation(event, context as any); + const responseIs5xxError = listener.onEndingInvocation(event, { statusCode: 500 }, false); + + expect(responseIs5xxError).toBe(true); + expect(mockProcessAppsecResponse).toHaveBeenCalledWith(mockSpan, { statusCode: 500 }, "500"); } finally { currentSpanSpy.mockRestore(); } diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 1483a6ad..2b2010aa 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -231,25 +231,29 @@ export class TraceListener { // Always clear the tree to prevent memory leaks, even if we skip span creation clearTraceTree(); } - if (this.config.appsecEnabled) { - processAppsecResponse(this.tracerWrapper.currentSpan, result); - } + // The status code has to be resolved and tagged before AppSec runs: the WAF needs the + // normalized value (raw result.statusCode is wrong for ALB, API Gateway v2 and response + // streaming), and API Security samples off the span, so http.status_code must already be + // there when the sampling decision is taken. + let statusCode: string | undefined; if (this.triggerTags) { - const statusCode = extractHTTPStatusCodeTag(this.triggerTags, result, isResponseStreamFunction); + statusCode = extractHTTPStatusCodeTag(this.triggerTags, result, isResponseStreamFunction); // Store the status tag in the listener to send to Xray on invocation completion this.triggerTags["http.status_code"] = statusCode!; if (this.tracerWrapper.currentSpan) { this.tracerWrapper.currentSpan.setTag("http.status_code", statusCode); } - if (this.inferredSpan) { - this.inferredSpan.setTag("http.status_code", statusCode); - - if (statusCode?.length === 3 && statusCode?.startsWith("5")) { - this.wrappedCurrentSpan.setTag("error", 1); - return true; - } - } + this.inferredSpan?.setTag("http.status_code", statusCode); + } + if (this.config.appsecEnabled) { + processAppsecResponse(this.tracerWrapper.currentSpan, result, statusCode); + } + // Kept behind AppSec so 5xx responses still reach the WAF, and still nested on inferredSpan + // so the early return only happens when there is an inferred span, as before. + if (this.inferredSpan && statusCode?.length === 3 && statusCode?.startsWith("5")) { + this.wrappedCurrentSpan.setTag("error", 1); + return true; } if (this.durableFunctionContext) { logDebug("Applying durable function context to the aws.lambda span"); From 3ec4f0b615df955dbbfb06ff8732062472a8ec4f Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Fri, 28 Aug 2026 15:32:15 +0200 Subject: [PATCH 2/7] refactor(appsec): move normalizeHeaders --- src/appsec/event-data-extractor.ts | 29 +------------------------ src/appsec/headers.ts | 35 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 src/appsec/headers.ts diff --git a/src/appsec/event-data-extractor.ts b/src/appsec/event-data-extractor.ts index d9b45321..084aa46c 100644 --- a/src/appsec/event-data-extractor.ts +++ b/src/appsec/event-data-extractor.ts @@ -1,4 +1,5 @@ import * as eventType from "../utils/event-type-guards"; +import { normalizeHeaders } from "./headers"; export interface ExtractedHTTPData { headers: Record; @@ -127,34 +128,6 @@ function extractFromLambdaUrl(event: any): ExtractedHTTPData { return result; } -function normalizeHeaders( - headers?: Record, - multiValueHeaders?: Record, -): Record { - if (!headers && !multiValueHeaders) return {}; - - const result: Record = {}; - - if (multiValueHeaders) { - for (const [key, values] of Object.entries(multiValueHeaders)) { - if (values && values.length > 0) { - result[key.toLowerCase()] = values.join(", "); - } - } - } - - if (headers) { - for (const [key, value] of Object.entries(headers)) { - const lowerKey = key.toLowerCase(); - if (!(lowerKey in result) && value !== undefined) { - result[lowerKey] = value; - } - } - } - - return result; -} - function separateCookies(headers: Record): { cookies: Record | undefined; headersNoCookies: Record; diff --git a/src/appsec/headers.ts b/src/appsec/headers.ts new file mode 100644 index 00000000..3601e336 --- /dev/null +++ b/src/appsec/headers.ts @@ -0,0 +1,35 @@ +/** + * Normalizes header names to lowercase and collapses multi-value headers into a single + * comma-separated value, which is the shape the WAF expects. When a name appears in both maps the + * multi-value one wins. + * + * API Gateway v1 and application load balancer payloads carry `headers` and `multiValueHeaders`, + * both on the request event and on the handler result. + */ +export function normalizeHeaders( + headers?: Record, + multiValueHeaders?: Record, +): Record { + if (!headers && !multiValueHeaders) return {}; + + const result: Record = {}; + + if (multiValueHeaders) { + for (const [key, values] of Object.entries(multiValueHeaders)) { + if (values && values.length > 0) { + result[key.toLowerCase()] = values.join(", "); + } + } + } + + if (headers) { + for (const [key, value] of Object.entries(headers)) { + const lowerKey = key.toLowerCase(); + if (!(lowerKey in result) && value !== undefined) { + result[lowerKey] = value; + } + } + } + + return result; +} From 32eb9a528826cad0713b04b9e277e9b7acb5e28b Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Fri, 28 Aug 2026 15:33:21 +0200 Subject: [PATCH 3/7] fix(appsec): normalize the response headers sent on end-invocation --- src/appsec/index.spec.ts | 28 ++++++++++++++++++++++++++++ src/appsec/index.ts | 15 ++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/appsec/index.spec.ts b/src/appsec/index.spec.ts index d44795c7..223f98a5 100644 --- a/src/appsec/index.spec.ts +++ b/src/appsec/index.spec.ts @@ -126,6 +126,34 @@ describe("AppSec orchestrator", () => { }); }); + it("should lowercase the response header names", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 200, headers: { "X-Option": "test_value" } }); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: { "x-option": "test_value" }, + }); + }); + + it("should merge multi value response headers", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { + statusCode: 200, + headers: { "Content-Type": "application/json" }, + multiValueHeaders: { "X-Option": ["a", "b"] }, + }); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: { "content-type": "application/json", "x-option": "a, b" }, + }); + }); + it("should fall back to the raw status code when no normalized one is given", () => { const span = { setTag: jest.fn() }; diff --git a/src/appsec/index.ts b/src/appsec/index.ts index 2aa4bbd1..68e0907f 100644 --- a/src/appsec/index.ts +++ b/src/appsec/index.ts @@ -2,6 +2,7 @@ const dc = require("dc-polyfill"); import { extractHTTPDataFromEvent } from "./event-data-extractor"; +import { normalizeHeaders } from "./headers"; const startInvocationChannel = dc.channel("datadog:lambda:start-invocation"); const endInvocationChannel = dc.channel("datadog:lambda:end-invocation"); @@ -42,6 +43,18 @@ export function processAppsecResponse(span: any, result: any, statusCode?: strin endInvocationChannel.publish({ span, statusCode: statusCode ?? result?.statusCode?.toString(), - responseHeaders: result?.headers as Record | undefined, + responseHeaders: normalizeResponseHeaders(result), }); } + +/** + * Response headers reach the tracer in the same shape as the request ones + */ +function normalizeResponseHeaders(result: any): Record | undefined { + const headers = result?.headers as Record | undefined; + const multiValueHeaders = result?.multiValueHeaders as Record | undefined; + + if (!headers && !multiValueHeaders) return undefined; + + return normalizeHeaders(headers, multiValueHeaders); +} From 52ea36f20cf20598524d63ad753eee0a32f8a5fc Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Fri, 28 Aug 2026 15:34:27 +0200 Subject: [PATCH 4/7] doc(appsec): fix comment on reordering in onEndingInvocation --- src/trace/listener.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 2b2010aa..5273bd88 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -231,10 +231,10 @@ export class TraceListener { // Always clear the tree to prevent memory leaks, even if we skip span creation clearTraceTree(); } - // The status code has to be resolved and tagged before AppSec runs: the WAF needs the - // normalized value (raw result.statusCode is wrong for ALB, API Gateway v2 and response - // streaming), and API Security samples off the span, so http.status_code must already be - // there when the sampling decision is taken. + // The status code has to be resolved and tagged before AppSec runs. The WAF needs the + // normalized value: a handler may leave statusCode out of its response, and only this call + // knows what to answer then (502 with no result at all, 200 otherwise). And API Security + // samples off the span, so http.status_code must already be there when the decision is taken. let statusCode: string | undefined; if (this.triggerTags) { statusCode = extractHTTPStatusCodeTag(this.triggerTags, result, isResponseStreamFunction); From 144f1ab9e001e0a1477a28504edaffd63047b01b Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Fri, 28 Aug 2026 16:05:31 +0200 Subject: [PATCH 5/7] fix(appsec): drop the status code fallback on end invocation --- src/appsec/index.spec.ts | 26 +++++++++++++++----------- src/appsec/index.ts | 5 ++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/appsec/index.spec.ts b/src/appsec/index.spec.ts index 223f98a5..d4cbef1c 100644 --- a/src/appsec/index.spec.ts +++ b/src/appsec/index.spec.ts @@ -78,10 +78,10 @@ describe("AppSec orchestrator", () => { expect(mockPublish).not.toHaveBeenCalled(); }); - it("should extract status code and headers from the result and publish them", () => { + it("should publish the normalized status code and the response headers", () => { const span = { setTag: jest.fn() }; - processAppsecResponse(span, { statusCode: 200, headers: { "content-type": "application/json" } }); + processAppsecResponse(span, { statusCode: 200, headers: { "content-type": "application/json" } }, "200"); expect(mockPublish).toHaveBeenCalledWith({ span, @@ -102,7 +102,7 @@ describe("AppSec orchestrator", () => { }); }); - it("should prefer the normalized status code over the raw one from the result", () => { + it("should ignore the status code carried by the result", () => { const span = { setTag: jest.fn() }; processAppsecResponse(span, { statusCode: 200 }, "502"); @@ -129,7 +129,7 @@ describe("AppSec orchestrator", () => { it("should lowercase the response header names", () => { const span = { setTag: jest.fn() }; - processAppsecResponse(span, { statusCode: 200, headers: { "X-Option": "test_value" } }); + processAppsecResponse(span, { statusCode: 200, headers: { "X-Option": "test_value" } }, "200"); expect(mockPublish).toHaveBeenCalledWith({ span, @@ -141,11 +141,15 @@ describe("AppSec orchestrator", () => { it("should merge multi value response headers", () => { const span = { setTag: jest.fn() }; - processAppsecResponse(span, { - statusCode: 200, - headers: { "Content-Type": "application/json" }, - multiValueHeaders: { "X-Option": ["a", "b"] }, - }); + processAppsecResponse( + span, + { + statusCode: 200, + headers: { "Content-Type": "application/json" }, + multiValueHeaders: { "X-Option": ["a", "b"] }, + }, + "200", + ); expect(mockPublish).toHaveBeenCalledWith({ span, @@ -154,14 +158,14 @@ describe("AppSec orchestrator", () => { }); }); - it("should fall back to the raw status code when no normalized one is given", () => { + it("should publish no status code when none is normalized, even if the result carries one", () => { const span = { setTag: jest.fn() }; processAppsecResponse(span, { statusCode: 204 }, undefined); expect(mockPublish).toHaveBeenCalledWith({ span, - statusCode: "204", + statusCode: undefined, responseHeaders: undefined, }); }); diff --git a/src/appsec/index.ts b/src/appsec/index.ts index 68e0907f..b5b97345 100644 --- a/src/appsec/index.ts +++ b/src/appsec/index.ts @@ -34,15 +34,14 @@ export function processAppsecRequest(event: any, span: any): void { /** * @param span * @param result - * @param statusCode Status code already normalized by the trigger layer. Falls back to the raw - * `result.statusCode` when the caller has none, which happens for non-HTTP triggers. + * @param statusCode Status code already normalized by the trigger layer. */ export function processAppsecResponse(span: any, result: any, statusCode?: string): void { if (!span || !endInvocationChannel.hasSubscribers) return; endInvocationChannel.publish({ span, - statusCode: statusCode ?? result?.statusCode?.toString(), + statusCode, responseHeaders: normalizeResponseHeaders(result), }); } From 7dae173c7da5f94c8bf314b6f12b57d4fe0166b3 Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Fri, 28 Aug 2026 16:14:06 +0200 Subject: [PATCH 6/7] doc(appsec): clean comments --- src/trace/listener.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 5273bd88..9766a867 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -231,10 +231,6 @@ export class TraceListener { // Always clear the tree to prevent memory leaks, even if we skip span creation clearTraceTree(); } - // The status code has to be resolved and tagged before AppSec runs. The WAF needs the - // normalized value: a handler may leave statusCode out of its response, and only this call - // knows what to answer then (502 with no result at all, 200 otherwise). And API Security - // samples off the span, so http.status_code must already be there when the decision is taken. let statusCode: string | undefined; if (this.triggerTags) { statusCode = extractHTTPStatusCodeTag(this.triggerTags, result, isResponseStreamFunction); From d19ad38d75d7b1d04a2f1efb8933e719685ceb70 Mon Sep 17 00:00:00 2001 From: Carles Capell Date: Mon, 31 Aug 2026 13:24:41 +0200 Subject: [PATCH 7/7] fix(appsec): harden response header normalization --- src/appsec/headers.ts | 10 +++++----- src/appsec/index.spec.ts | 36 ++++++++++++++++++++++++++++++++++++ src/appsec/index.ts | 4 ++-- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/appsec/headers.ts b/src/appsec/headers.ts index 3601e336..1f2002d1 100644 --- a/src/appsec/headers.ts +++ b/src/appsec/headers.ts @@ -7,8 +7,8 @@ * both on the request event and on the handler result. */ export function normalizeHeaders( - headers?: Record, - multiValueHeaders?: Record, + headers?: Record, + multiValueHeaders?: Record, ): Record { if (!headers && !multiValueHeaders) return {}; @@ -16,7 +16,7 @@ export function normalizeHeaders( if (multiValueHeaders) { for (const [key, values] of Object.entries(multiValueHeaders)) { - if (values && values.length > 0) { + if (Array.isArray(values) && values.length > 0) { result[key.toLowerCase()] = values.join(", "); } } @@ -25,8 +25,8 @@ export function normalizeHeaders( if (headers) { for (const [key, value] of Object.entries(headers)) { const lowerKey = key.toLowerCase(); - if (!(lowerKey in result) && value !== undefined) { - result[lowerKey] = value; + if (!(lowerKey in result) && value !== undefined && value !== null) { + result[lowerKey] = String(value); } } } diff --git a/src/appsec/index.spec.ts b/src/appsec/index.spec.ts index d4cbef1c..694f1bb5 100644 --- a/src/appsec/index.spec.ts +++ b/src/appsec/index.spec.ts @@ -158,6 +158,42 @@ describe("AppSec orchestrator", () => { }); }); + it("should ignore multi value response headers that are not arrays", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 200, multiValueHeaders: { "Set-Cookie": "a=b" } }, "200"); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: {}, + }); + }); + + it("should stringify non string response header values", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 200, headers: { "Content-Length": 42, "X-Flag": true } }, "200"); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: { "content-length": "42", "x-flag": "true" }, + }); + }); + + it("should skip response headers with a null value", () => { + const span = { setTag: jest.fn() }; + + processAppsecResponse(span, { statusCode: 200, headers: { "X-Option": null } }, "200"); + + expect(mockPublish).toHaveBeenCalledWith({ + span, + statusCode: "200", + responseHeaders: {}, + }); + }); + it("should publish no status code when none is normalized, even if the result carries one", () => { const span = { setTag: jest.fn() }; diff --git a/src/appsec/index.ts b/src/appsec/index.ts index b5b97345..c28a0951 100644 --- a/src/appsec/index.ts +++ b/src/appsec/index.ts @@ -50,8 +50,8 @@ export function processAppsecResponse(span: any, result: any, statusCode?: strin * Response headers reach the tracer in the same shape as the request ones */ function normalizeResponseHeaders(result: any): Record | undefined { - const headers = result?.headers as Record | undefined; - const multiValueHeaders = result?.multiValueHeaders as Record | undefined; + const headers = result?.headers as Record | undefined; + const multiValueHeaders = result?.multiValueHeaders as Record | undefined; if (!headers && !multiValueHeaders) return undefined;