mirror of
https://github.com/cline/cline.git
synced 2026-09-15 04:14:34 +08:00
fix(shared): keep valid OTEL headers when one entry is malformed (#12260)
parseKeyPairsIntoRecord wrapped the whole forEach in one try/catch, so a single entry that broke decodeURIComponent (e.g. a stray % in OTEL_EXPORTER_OTLP_HEADERS) aborted the loop and silently dropped every remaining header. Move the try/catch inside the loop to skip only the malformed entry. Adds regression tests.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseKeyPairsIntoRecord } from "./utils";
|
||||
|
||||
describe("parseKeyPairsIntoRecord", () => {
|
||||
it("parses comma-separated key=value pairs", () => {
|
||||
expect(parseKeyPairsIntoRecord("a=1,b=2,c=3")).toEqual({
|
||||
a: "1",
|
||||
b: "2",
|
||||
c: "3",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an empty record for empty or undefined input", () => {
|
||||
expect(parseKeyPairsIntoRecord("")).toEqual({});
|
||||
expect(parseKeyPairsIntoRecord(undefined)).toEqual({});
|
||||
});
|
||||
|
||||
it("trims whitespace and decodes percent-encoded keys and values", () => {
|
||||
expect(parseKeyPairsIntoRecord(" Authorization = Bearer%20abc ")).toEqual({
|
||||
Authorization: "Bearer abc",
|
||||
});
|
||||
});
|
||||
|
||||
it("skips entries without a separator or with an empty key", () => {
|
||||
expect(parseKeyPairsIntoRecord("novalue,=nokey,good=1")).toEqual({
|
||||
good: "1",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps well-formed entries when a later entry is malformed", () => {
|
||||
// `b=%` is an invalid percent-encoding: decodeURIComponent throws on it.
|
||||
// The malformed entry must be skipped without discarding `a` or `c`.
|
||||
expect(parseKeyPairsIntoRecord("a=1,b=%,c=3")).toEqual({
|
||||
a: "1",
|
||||
c: "3",
|
||||
});
|
||||
});
|
||||
|
||||
it("skips only the malformed pair in a realistic OTEL header string", () => {
|
||||
expect(
|
||||
parseKeyPairsIntoRecord(
|
||||
"Authorization=Bearer abc,X-Tenant=acme%,X-Trace=on",
|
||||
),
|
||||
).toEqual({
|
||||
Authorization: "Bearer abc",
|
||||
"X-Trace": "on",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,11 +7,11 @@ export function parseKeyPairsIntoRecord(
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
value.split(",").forEach((entry) => {
|
||||
const separatorIndex = entry.indexOf("=");
|
||||
if (separatorIndex <= 0) return;
|
||||
value.split(",").forEach((entry) => {
|
||||
const separatorIndex = entry.indexOf("=");
|
||||
if (separatorIndex <= 0) return;
|
||||
|
||||
try {
|
||||
// From the beginning to the equal sign is the key, from the equal sign to the end is the value
|
||||
const key = decodeURIComponent(entry.substring(0, separatorIndex).trim());
|
||||
const value = decodeURIComponent(
|
||||
@@ -21,8 +21,12 @@ export function parseKeyPairsIntoRecord(
|
||||
if (!key || !value) return;
|
||||
|
||||
result[key] = value;
|
||||
});
|
||||
} catch {}
|
||||
} catch {
|
||||
// Skip a single malformed entry (e.g. an invalid percent-encoding
|
||||
// that makes decodeURIComponent throw) rather than aborting the
|
||||
// whole list and silently dropping every remaining pair.
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user