@@ -104,7 +104,7 @@ func WrapWindowsProductionJSONWarningHookCommand(command string, format WarningF
104104
105105 return fmt .Sprintf (
106106 `cmd.exe /d /s /c "where.exe entire >nul 2>nul & if errorlevel 1 (echo %s) else (%s)"` ,
107- escapeWindowsCMD (string (payload )),
107+ escapeWindowsCMDForNestedCommand (string (payload )),
108108 command ,
109109 )
110110}
@@ -114,7 +114,7 @@ func WrapWindowsProductionJSONWarningHookCommand(command string, format WarningF
114114func WrapWindowsProductionPlainTextWarningHookCommand (command string , format WarningFormat ) string {
115115 return fmt .Sprintf (
116116 `cmd.exe /d /s /c "where.exe entire >nul 2>nul & if errorlevel 1 (echo %s) else (%s)"` ,
117- escapeWindowsCMD (windowsPlainTextWarning (format )),
117+ escapeWindowsCMDForNestedCommand (windowsPlainTextWarning (format )),
118118 command ,
119119 )
120120}
@@ -171,16 +171,13 @@ func hasManagedHookPrefix(command string, prefixes []string) bool {
171171 return false
172172}
173173
174- // escapeWindowsCMD caret-escapes the cmd.exe block metacharacters that would
175- // otherwise terminate the `(echo …)` warning block or redirect its output.
174+ // escapeWindowsCMD caret-escapes one cmd.exe parsing layer's block
175+ // metacharacters so they cannot terminate the `(echo …)` warning block or
176+ // redirect its output.
176177//
177- // `%` is deliberately NOT escaped. These wrappers are a `cmd.exe /d /s /c`
178- // command line, not a batch script, so batch's `%%` doubling does not apply
179- // (it would print a literal `%%`), and caret-escaping `%` is not a thing cmd
180- // recognizes — `^%` would leak the caret. On the command line a lone `%` is
181- // literal and `%NAME%` only expands for a defined environment variable, so the
182- // fixed, %-free warning constant is emitted verbatim. If the warning text ever
183- // gains a `%NAME%` that collides with a real env var, revisit this.
178+ // `%` is deliberately NOT escaped because caret-escaping `%` is not something
179+ // cmd.exe recognizes — `^%` would leak the caret. The fixed warning constants
180+ // are %-free; if that changes, percent expansion needs separate handling.
184181func escapeWindowsCMD (s string ) string {
185182 replacer := strings .NewReplacer (
186183 `^` , `^^` ,
@@ -195,6 +192,13 @@ func escapeWindowsCMD(s string) string {
195192 return replacer .Replace (s )
196193}
197194
195+ // escapeWindowsCMDForNestedCommand preserves metacharacter escaping through
196+ // the hook runner's outer cmd.exe /c so the nested cmd.exe /d /s /c receives
197+ // one complete escape layer of its own.
198+ func escapeWindowsCMDForNestedCommand (s string ) string {
199+ return escapeWindowsCMD (escapeWindowsCMD (s ))
200+ }
201+
198202func windowsPlainTextWarning (format WarningFormat ) string {
199203 return strings .Join (strings .Fields (MissingEntireWarning (format )), " " )
200204}
0 commit comments