fix(process): keep ports before a chunk-final period

PTY chunks are normalized independently. A period that ends a chunk may
begin a decimal continued in the next chunk ("port: 80." then "5"), so
leave such a port unnormalized instead of masking the decimal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-09 14:19:37 +00:00
parent db98a51dd1
commit 184ed970b9
3 changed files with 13 additions and 3 deletions

View File

@ -114,7 +114,8 @@ Port normalization recognizes explicit `port`, `tcp_port`, `udp_port`, and
`listen` fields, URL authorities, IP endpoints, and `localhost` endpoints, with
ports from 0 through 65535. A port may end a sentence, as in `port: 8080.`;
a period followed by a letter or digit continues a decimal, version, or host
name instead. It preserves ordinary counters, dimensions, file line numbers,
name instead. Each PTY chunk is normalized on its own, so a port whose period
ends the chunk is left unchanged because its continuation is not yet known. It preserves ordinary counters, dimensions, file line numbers,
and ambiguous bare host labels. Use explicit literal `patterns`
for a different application-specific spelling. Set `ports: false` to preserve
all endpoint numbers in comparison text. Captures commit the port-normalization

View File

@ -118,6 +118,13 @@ it("normalizes ports that end a sentence but not decimal or host continuations",
);
});
it("keeps a port before a chunk-final period, whose continuation is unknown", () => {
for (const chunk of ["port: 80.", "http://localhost:8080.", "[::1]:3000."])
expect(
normalizeProcessText(chunk, scenario({}), "/temporary", rootPid),
).toBe(chunk);
});
it("normalizes representative process identities and preserves null, zero, and unrelated numbers", () => {
const before = JSON.stringify(samples);
const normalized = normalizeProcessSamples(samples, scenario({}), rootPid);

View File

@ -43,9 +43,11 @@ export const normalizeProcessText = (
// 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.
// 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})(?![\w@:]|\.(?:\w|$))/giu,
(match: string, endpoint: string, port: string) => {
if (Number(port) > 65_535) return match;
const isEndpoint = endpoint.includes("://")
@ -56,7 +58,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})(?!\w|\.(?:\w|$))/giu,
(match: string, prefix: string, port: string) =>
Number(port) <= 65_535 ? `${prefix}<port>` : match,
);