diff --git a/lib/regex/regex.go b/lib/regex/regex.go index 98e0e5e..ebb5e61 100644 --- a/lib/regex/regex.go +++ b/lib/regex/regex.go @@ -605,9 +605,14 @@ func (m *Match) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: func (m *Match) groupIndex(v starlark.Value) (int, error) { switch g := v.(type) { case starlark.Int: - i, _ := g.Int64() - if i < 0 || int(i) > m.p.search.NumSubexp() { - return 0, fmt.Errorf("no such group: %d", i) + // Int64 reports ok=false when the index overflows int64; the + // discarded ok used to leave i==0, silently selecting group 0 (the + // whole match) for a huge index instead of erroring. Compare as + // int64 to avoid truncating on the way to int, and format the + // original value (an overflowing index prints as 0 via %d). + i, ok := g.Int64() + if !ok || i < 0 || i > int64(m.p.search.NumSubexp()) { + return 0, fmt.Errorf("no such group: %s", g.String()) } return int(i), nil case starlark.String: diff --git a/lib/regex/regex_test.go b/lib/regex/regex_test.go index 770c800..c0edf98 100644 --- a/lib/regex/regex_test.go +++ b/lib/regex/regex_test.go @@ -233,6 +233,21 @@ func TestLoadModule_Regex(t *testing.T) { `), wantErr: `no such group: 5`, }, + { + // a group index too large for int64 used to be silently truncated + // to 0 (the whole match) instead of erroring; group/start/end/span + // all route through groupIndex and must reject it loudly. + name: `error: group index overflows int64`, + script: itn.HereDoc(` + load('regex', 'search') + m = search('(a)', 'a') + assert.eq(m.group(1), 'a') + assert.fails(lambda: m.group(1 << 70), 'no such group') + assert.fails(lambda: m.start(1 << 70), 'no such group') + assert.fails(lambda: m.end(1 << 70), 'no such group') + assert.fails(lambda: m.span(1 << 70), 'no such group') + `), + }, { name: `error: bad repl type`, script: itn.HereDoc(`