From 39e362eea5603fa36bda436c9f03d9304960c1ea Mon Sep 17 00:00:00 2001 From: SkyZeroZx <73321943+SkyZeroZx@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:53:40 -0500 Subject: [PATCH] fix(http): match header values exactly when deleting Normalize value-specific HttpHeaders deletions before filtering. The string overload previously used String#indexOf and removed shorter values contained within the requested deletion value, potentially widening outgoing request metadata. Preserve delete-all behavior only when no value is supplied, and cover string, array, and empty-string deletion. (cherry picked from commit f33ee95045ac3ab1e96baa41f44dd130ef3724ab) --- packages/common/http/src/headers.ts | 7 +++--- packages/common/http/test/headers_spec.ts | 28 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/common/http/src/headers.ts b/packages/common/http/src/headers.ts index 5aabc1cd245..ed6e7c8018a 100644 --- a/packages/common/http/src/headers.ts +++ b/packages/common/http/src/headers.ts @@ -223,16 +223,17 @@ export class HttpHeaders { this.headers.set(key, base); break; case 'd': - const toDelete = update.value as string | undefined; - if (!toDelete) { + const toDelete = update.value; + if (toDelete === undefined) { this.headers.delete(key); this.normalizedNames.delete(key); } else { + const valuesToDelete = Array.isArray(toDelete) ? toDelete : [toDelete]; let existing = this.headers.get(key); if (!existing) { return; } - existing = existing.filter((value) => toDelete.indexOf(value) === -1); + existing = existing.filter((value) => valuesToDelete.indexOf(value) === -1); if (existing.length === 0) { this.headers.delete(key); this.normalizedNames.delete(key); diff --git a/packages/common/http/test/headers_spec.ts b/packages/common/http/test/headers_spec.ts index 4205cc04fcf..58af75292be 100644 --- a/packages/common/http/test/headers_spec.ts +++ b/packages/common/http/test/headers_spec.ts @@ -154,6 +154,34 @@ describe('HttpHeaders', () => { const fourth = third.delete('FOO'); expect(fourth.has('foo')).toEqual(false); }); + + it('should delete only the exact matching string value', () => { + const headers = new HttpHeaders({ + 'X-Scopes': ['tenant:alpha', 'tenant:alpha:archive'], + }); + + const updated = headers.delete('X-Scopes', 'tenant:alpha:archive'); + + expect(updated.getAll('X-Scopes')).toEqual(['tenant:alpha']); + }); + + it('should delete only exact matching values from an array', () => { + const headers = new HttpHeaders({ + 'X-Scopes': ['tenant:alpha', 'tenant:alpha:archive'], + }); + + const updated = headers.delete('X-Scopes', ['tenant:alpha:archive']); + + expect(updated.getAll('X-Scopes')).toEqual(['tenant:alpha']); + }); + + it('should treat an empty string as a value to delete', () => { + const headers = new HttpHeaders({foo: ['', 'bar']}); + + const updated = headers.delete('foo', ''); + + expect(updated.getAll('foo')).toEqual(['bar']); + }); }); describe('.append', () => {