From b412a450538e5f4a1d7057732e75d945dafa18a6 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Thu, 22 Oct 2026 08:17:27 +0900 Subject: [PATCH] Avoid O(n^2) string concatenation in maskActionContents - `masked +=` per token copied the whole prefix on every append - ~1 MB POST to --listen blocked the server for ~30 s - switch to strings.Builder Reported by Zachariah Mithani (@zachariah-mithani, AploSec). --- CHANGELOG.md | 1 + src/options.go | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ba87dd..01874e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ CHANGELOG - The completion integration now requires nushell 0.116.1 or above, where an interactive completer can take the terminal through the `@interactive` attribute (#4934) (@sim590) - Fixed `--no-sort` being ignored in filter mode with `--sync`, `--tac`, or `--bench` (#4940) (@NavpreetSSidhu) - Vim plugin: Fixed garbage text on screen in Neovim with `set title` when the fzf command contains ANSI escape sequences (#4941) +- Fixed O(n^2) action parsing in `--listen`. A single ~1 MB request could block the server for ~30 s (@zachariah-mithani, AploSec) 0.74.5 ------ diff --git a/src/options.go b/src/options.go index 94e3a7f9..52986b2c 100644 --- a/src/options.go +++ b/src/options.go @@ -1678,15 +1678,15 @@ func init() { } func maskActionContents(action string) string { - masked := "" + var sb strings.Builder Loop: for len(action) > 0 { loc := argActionRegexp.FindStringIndex(action) if loc == nil { - masked += action + sb.WriteString(action) break } - masked += action[:loc[1]] + sb.WriteString(action[:loc[1]]) action = action[loc[1]:] if len(action) == 0 { break @@ -1695,7 +1695,7 @@ Loop: var ce string switch action[0] { case ':': - masked += strings.Repeat(" ", len(action)) + sb.WriteString(strings.Repeat(" ", len(action))) break Loop case '(': ce = ")" @@ -1716,7 +1716,7 @@ Loop: // @$ or @+ loc = regexp.MustCompile(fmt.Sprintf(`(?s)^%s.*?(%s[+,]|%s$)`, cs, ce, ce)).FindStringIndex(action) if loc == nil { - masked += action + sb.WriteString(action) break } // Keep + or , at the end @@ -1724,9 +1724,10 @@ Loop: if lastChar == '+' || lastChar == ',' { loc[1]-- } - masked += strings.Repeat(" ", loc[1]) + sb.WriteString(strings.Repeat(" ", loc[1])) action = action[loc[1]:] } + masked := sb.String() masked = strings.ReplaceAll(masked, ",,,", string([]rune{',', escapedComma, ','})) masked = strings.ReplaceAll(masked, ",:,", string([]rune{',', escapedColon, ','})) masked = strings.ReplaceAll(masked, "::", string([]rune{escapedColon, ':'}))