@@ -200,23 +200,50 @@ loop:
200200 backtick = ""
201201 backQuote = ! backQuote
202202 }
203+
203204 case ')' :
204205 if ! singleQuoted && ! doubleQuoted && ! backQuote {
205206 if p .ParseBacktick {
206- if dollarQuote {
207- out , err := shellRun (backtick , p .Dir )
208- if err != nil {
209- return nil , err
210- }
211- buf = buf [:len (buf )- len (backtick )- 2 ] + out
207+ // Hardened fix:
208+ // A bare ')' must never open dollarQuote state.
209+ // Only close an already-open $(...) region.
210+ if ! dollarQuote {
211+ buf += string (r )
212+ got = argSingle
213+ continue
214+ }
215+
216+ out , err := shellRun (backtick , p .Dir )
217+ if err != nil {
218+ return nil , err
219+ }
220+
221+ // Defensive guard: valid $(...) implies the buffer must contain
222+ // the "$(" prefix plus the collected command body.
223+ if len (buf ) < len (backtick )+ 2 {
224+ return nil , errors .New ("invalid command line string" )
212225 }
226+
227+ buf = buf [:len (buf )- len (backtick )- 2 ] + out
213228 backtick = ""
214- dollarQuote = ! dollarQuote
229+ dollarQuote = false
215230 continue
216231 }
232+
233+ // Default mode:
234+ // Do not toggle dollarQuote on a bare ')'.
235+ // Treat it as a literal character.
236+ if ! dollarQuote {
237+ buf += string (r )
238+ got = argSingle
239+ continue
240+ }
241+
217242 backtick = ""
218- dollarQuote = ! dollarQuote
243+ dollarQuote = false
244+ continue
219245 }
246+
220247 case '(' :
221248 if ! singleQuoted && ! doubleQuoted && ! backQuote {
222249 if ! dollarQuote && strings .HasSuffix (buf , "$" ) {
@@ -227,6 +254,7 @@ loop:
227254 return nil , errors .New ("invalid command line string" )
228255 }
229256 }
257+
230258 case '"' :
231259 if ! singleQuoted && ! dollarQuote {
232260 if doubleQuoted {
@@ -235,6 +263,7 @@ loop:
235263 doubleQuoted = ! doubleQuoted
236264 continue
237265 }
266+
238267 case '\'' :
239268 if ! doubleQuoted && ! dollarQuote {
240269 if singleQuoted {
@@ -243,6 +272,7 @@ loop:
243272 singleQuoted = ! singleQuoted
244273 continue
245274 }
275+
246276 case ';' , '&' , '|' , '<' , '>' :
247277 if ! (escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote ) {
248278 if r == '>' && len (buf ) > 0 {
0 commit comments