diff --git a/docs/process-capture.md b/docs/process-capture.md index 69a39c64f..24cdac571 100644 --- a/docs/process-capture.md +++ b/docs/process-capture.md @@ -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 diff --git a/src/process/capture/ProcessNormalization.test.ts b/src/process/capture/ProcessNormalization.test.ts index b07a228a3..006d8bde4 100644 --- a/src/process/capture/ProcessNormalization.test.ts +++ b/src/process/capture/ProcessNormalization.test.ts @@ -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); diff --git a/src/process/capture/ProcessNormalization.ts b/src/process/capture/ProcessNormalization.ts index 27c7d1f49..8423b5dd1 100644 --- a/src/process/capture/ProcessNormalization.ts +++ b/src/process/capture/ProcessNormalization.ts @@ -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}` : match, );