fix(process): normalize scenario replacements literally

capture_process_scenario declares normalization patterns as literal text, but\nreplaceAll expands \$&\, \$\`, and \$1\ in the replacement as a replacement\npattern. A caller normalizing text that contains a dollar sign silently gets\nduplicated, spliced, or dropped content in the recorded Evidence instead of\nthe requested literal replacement.\n\nUse split/join, which is literal on both sides and is already the idiom used\nfor exact-literal scenario rules in browserScenarioNormalization.ts.
This commit is contained in:
openhands authored and Ysopwnable committed 2026-10-10 16:57:06 +08:00
1 parent 2bf812772d
commit f51503ef1c
2 files changed
+28 -1

No files matched your search

@@ -71,6 +71,30 @@ const scenario = (normalization: {
},
});
it("replaces caller-declared patterns literally, without expanding replacement patterns", () => {
const declared = parseProcessScenario({
executable: "/usr/bin/node",
working_directory: "/workspace",
normalization: {
paths: false,
time_bucket_ms: 10,
patterns: [
{ pattern: "8080", replacement: "$&$&" },
{ pattern: "READY", replacement: "$$" },
{ pattern: "NAME", replacement: "$1" },
],
},
});
expect(
normalizeProcessText(
"listening on 8080; READY; NAME=alpha; tail NAME",
declared,
"/temporary",
rootPid,
),
).toBe("listening on $&$&; $$; $1=alpha; tail $1");
});
it("preserves compact JSON, counters, line numbers, and ambiguous endpoint spellings", () => {
const input = [
'{"pid":32,"size":108,"height":480,"width":640,"score":20}',
+4 -1
View File
@@ -34,8 +34,11 @@ export const normalizeProcessText = (
normalized = normalizePortTokens(normalized);
if (scenario.normalization.pids)
normalized = normalizePidTokens(normalized, [pid]);
// Scenario replacements are literal text, so `$&`, `` $` ``, and `$1` must
// reach the captured output verbatim instead of expanding as a replacement
// pattern. split/join is literal on both sides.
for (const pattern of scenario.normalization.patterns)
normalized = normalized.replaceAll(pattern.pattern, pattern.replacement);
normalized = normalized.split(pattern.pattern).join(pattern.replacement);
return normalized;
};