From ad813e473b094ab0d3ab30798b7d252e2f4beb94 Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:28:19 +0000 Subject: [PATCH] fix(process): treat non-ASCII letters as port continuations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit \w is ASCII-only even with the u flag, so "port: 8080.β" was treated as a sentence-ending port. Use Unicode letter and number classes for the port boundary checks. Co-Authored-By: Claude Opus 5.5 --- src/process/capture/ProcessNormalization.test.ts | 2 ++ src/process/capture/ProcessNormalization.ts | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/process/capture/ProcessNormalization.test.ts b/src/process/capture/ProcessNormalization.test.ts index 006d8bde40..8598a91f1f 100644 --- a/src/process/capture/ProcessNormalization.test.ts +++ b/src/process/capture/ProcessNormalization.test.ts @@ -106,6 +106,7 @@ it("normalizes ports that end a sentence but not decimal or host continuations", "Bound [::1]:3000.", "listen=9000.\n", "port: 80.5 localhost:8080.5 localhost:8080.example.test", + "port: 8080.β localhost:8080.é port: 8080é", ].join("\n"); expect(normalizeProcessText(input, scenario({}), "/temporary", rootPid)).toBe( [ @@ -114,6 +115,7 @@ it("normalizes ports that end a sentence but not decimal or host continuations", "Bound [::1]:.", "listen=.\n", "port: 80.5 localhost:8080.5 localhost:8080.example.test", + "port: 8080.β localhost:8080.é port: 8080é", ].join("\n"), ); }); diff --git a/src/process/capture/ProcessNormalization.ts b/src/process/capture/ProcessNormalization.ts index 8423b5dd1b..675ac95295 100644 --- a/src/process/capture/ProcessNormalization.ts +++ b/src/process/capture/ProcessNormalization.ts @@ -41,13 +41,14 @@ export const normalizeProcessText = ( return normalized; }; -// A port may end a sentence, but a period followed by a word character -// continues a decimal, version, or host name and is not a port boundary. +// A port may end a sentence, but a period followed by a letter, digit, or +// underscore in any script continues a decimal, version, or host name and is +// not a port boundary. // PTY chunks are normalized independently, so a period that ends the chunk // stays unnormalized: its continuation may arrive in the next chunk. const normalizePortTokens = (value: string): string => { const endpointNormalized = value.replaceAll( - /(\b[a-z][a-z0-9+.-]*:\/\/[^\s/?#"'<>]+|\blocalhost|\b(?:\d{1,3}\.){3}\d{1,3}|\[[\da-f:.%]+\]):(\d{1,5})(?![\w@:]|\.(?:\w|$))/giu, + /(\b[a-z][a-z0-9+.-]*:\/\/[^\s/?#"'<>]+|\blocalhost|\b(?:\d{1,3}\.){3}\d{1,3}|\[[\da-f:.%]+\]):(\d{1,5})(?![\p{L}\p{N}_@:]|\.(?:[\p{L}\p{N}_]|$))/giu, (match: string, endpoint: string, port: string) => { if (Number(port) > 65_535) return match; const isEndpoint = endpoint.includes("://") @@ -58,7 +59,7 @@ const normalizePortTokens = (value: string): string => { }, ); return endpointNormalized.replaceAll( - /(\b(?:port|tcp_port|udp_port|listen)\b["']?\s*[:=]\s*)(\d{1,5})(?!\w|\.(?:\w|$))/giu, + /(\b(?:port|tcp_port|udp_port|listen)\b["']?\s*[:=]\s*)(\d{1,5})(?![\p{L}\p{N}_]|\.(?:[\p{L}\p{N}_]|$))/giu, (match: string, prefix: string, port: string) => Number(port) <= 65_535 ? `${prefix}` : match, );