From 348e24d22d6b58aa5dcb06985df6fcb7a1d1881d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 19 May 2026 10:15:23 +0200 Subject: [PATCH] browser/tools: sort env vars by length for reverse substitution Sorts environment variables by value length descending to prevent shorter values from clobbering longer ones during substitution. --- src/browser/tools.zig | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/browser/tools.zig b/src/browser/tools.zig index 08f6273d..5a295833 100644 --- a/src/browser/tools.zig +++ b/src/browser/tools.zig @@ -1232,14 +1232,31 @@ pub fn substituteEnvVars(arena: std.mem.Allocator, input: []const u8) error{OutO /// chars are skipped to avoid false-positive substring matches. pub fn reverseSubstituteEnvVars(arena: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]const u8 { const env_names = lpEnvNames() catch return input; - var current: []const u8 = input; - var changed = false; + + // Iterate by value length descending. With two LP_* values where one is a + // substring of the other (both ≥4 chars so neither is filtered), name-order + // iteration would let the shorter value clobber part of the longer one + // before its full match is found, leaking a suffix into the recording. + const Pair = struct { name: []const u8, value: []const u8 }; + var pairs: std.ArrayList(Pair) = .empty; + try pairs.ensureTotalCapacity(arena, env_names.len); for (env_names) |name| { const value = lookupLpEnv(name) orelse continue; if (value.len < 4) continue; - if (std.mem.indexOf(u8, current, value) == null) continue; - const placeholder = try std.fmt.allocPrint(arena, "${s}", .{name}); - current = try std.mem.replaceOwned(u8, arena, current, value, placeholder); + pairs.appendAssumeCapacity(.{ .name = name, .value = value }); + } + std.mem.sort(Pair, pairs.items, {}, struct { + fn lt(_: void, a: Pair, b: Pair) bool { + return a.value.len > b.value.len; + } + }.lt); + + var current: []const u8 = input; + var changed = false; + for (pairs.items) |p| { + if (std.mem.indexOf(u8, current, p.value) == null) continue; + const placeholder = try std.fmt.allocPrint(arena, "${s}", .{p.name}); + current = try std.mem.replaceOwned(u8, arena, current, p.value, placeholder); changed = true; } return if (changed) current else input;