--- CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed a non-terminating loop in the selector lexer when tokenizing unterminated regex patterns (e.g. `r/abc`). The tokenizer now returns an error instead of looping indefinitely. - Fix panic when selector query contains a trailing backslash in a quoted string ([GHSA-m5j3-4634-c2vq](https://github.com/TomWright/dasel/security/advisories/GHSA-m5j3-4634-c2vq)). ## [v3.10.0] - 2026-05-13 --- selector/lexer/tokenize.go @@ -271,17 +271,20 @@ func (p *Tokenizer) parseCurRune() (Token, error) { return ptr.To(NewToken(kind, other, pos, l)) } - matchRegexPattern := func(pos int) *Token { + matchRegexPattern := func(pos int) (*Token, error) { if p.src[pos] != 'r' || !p.peekRuneEqual(pos+1, '/') { - return nil + return nil, nil } start := pos pos += 2 - for !p.peekRuneEqual(pos, '/') { + for pos < p.srcLen && p.src[pos] != '/' { pos++ } + if pos >= p.srcLen { + return nil, &UnexpectedEOFError{Pos: pos} + } pos++ - return ptr.To(NewToken(RegexPattern, p.src[start+2:pos-1], start, pos-start)) + return ptr.To(NewToken(RegexPattern, p.src[start+2:pos-1], start, pos-start)), nil } if t := matchStr(pos, "null", true, Null); t != nil { @@ -345,7 +348,10 @@ func (p *Tokenizer) parseCurRune() (Token, error) { return *t, nil } - if t := matchRegexPattern(pos); t != nil { + if t, err := matchRegexPattern(pos); t != nil || err != nil { + if err != nil { + return Token{}, err + } return *t, nil } --- selector/lexer/tokenize_test.go @@ -181,5 +181,13 @@ func TestTokenizer_Parse(t *testing.T) { in: `'\`, match: matchUnexpectedEOFError(2), }.run) + t.Run("unterminated regex", errTestCase{ + in: `r/unterminated`, + match: matchUnexpectedEOFError(14), + }.run) + t.Run("unterminated regex minimal", errTestCase{ + in: `r/`, + match: matchUnexpectedEOFError(2), + }.run) }) }