Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions clients/cli/__tests__/error-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,17 @@ describe("envelope URL redaction", () => {
expect(envelope.message).not.toContain("tok456");
});

it("keeps a punctuation run inside the URL and splits only the trailing one", () => {
// A long run that does not end the match was quadratic under the old
// unanchored /[…]+$/ (#2540); the backward scan must still stop at `x`.
const run = "!".repeat(50_000);
const { envelope } = classifyError(
new Error(`see https://srv.example/cb?note=${run}x&code=abc123!?`),
);
expect(envelope.message).toMatch(/x&code=%5BREDACTED%5D!\?$/);
expect(envelope.message).not.toContain("abc123");
});

it("redacts a URL whose scheme is upper- or mixed-case", () => {
const { envelope } = classifyError(
new Error(
Expand Down
31 changes: 27 additions & 4 deletions clients/cli/src/error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,31 @@ const UNREACHABLE_PATTERN =
const EMBEDDED_URL_PATTERN = /\bhttps?:\/\/(?:(?!https?:\/\/)[^\s"<>])+/gi;

/** Sentence punctuation (or a closing quote) a message may put right after a URL. */
const TRAILING_PUNCTUATION = /[.,;:!?)\]']+$/;
const TRAILING_PUNCTUATION = new Set([
".",
",",
";",
":",
"!",
"?",
")",
"]",
"'",
]);

/**
* Length of `match` once its trailing {@link TRAILING_PUNCTUATION} run is
* removed. A backward scan rather than an unanchored `/[…]+$/`: that regex
* rescans a punctuation run from every start position when the run does not
* end the string, which is quadratic, and the text here is server-controlled
* (an HTTP error body lands in the message), so a long `!!!…x` stalled the
* CLI's error path (#2540).
*/
function trailingPunctuationStart(match: string): number {
let end = match.length;
while (end > 0 && TRAILING_PUNCTUATION.has(match.charAt(end - 1))) end--;
return end;
}

/**
* Apply {@link redactUrlQuery} to every URL embedded in `text`. Trailing
Expand All @@ -166,9 +190,8 @@ const TRAILING_PUNCTUATION = /[.,;:!?)\]']+$/;
*/
function redactUrlsInText(text: string): string {
return text.replace(EMBEDDED_URL_PATTERN, (match) => {
const trailing = TRAILING_PUNCTUATION.exec(match)?.[0] ?? "";
const url = match.slice(0, match.length - trailing.length);
return redactUrlQuery(url) + trailing;
const end = trailingPunctuationStart(match);
return redactUrlQuery(match.slice(0, end)) + match.slice(end);
});
}

Expand Down
Loading