Skip to content

Commit ac6ae93

Browse files
committed
Fail random_id on repeated entropy rejection
1 parent 14bdeb0 commit ac6ae93

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

vibes/builtins.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
const (
1818
randomIDAlphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
1919
randomIDUnbiasedCutoff = byte((256 / len(randomIDAlphabet)) * len(randomIDAlphabet))
20+
maxRandomIDStallReads = 8
2021
maxJSONPayloadBytes = 1 << 20
2122
maxRegexInputBytes = 1 << 20
2223
maxRegexPatternSize = 16 << 10
@@ -130,21 +131,32 @@ func builtinRandomID(exec *Execution, receiver Value, args []Value, kwargs map[s
130131
}
131132

132133
chars := make([]byte, 0, length)
134+
stalledReads := 0
133135
for int64(len(chars)) < length {
134136
needed := int(length) - len(chars)
135137
raw, err := exec.engine.randomBytes(needed)
136138
if err != nil {
137139
return NewNil(), err
138140
}
141+
acceptedThisRead := 0
139142
for _, b := range raw {
140143
if b >= randomIDUnbiasedCutoff {
141144
continue
142145
}
143146
chars = append(chars, randomIDAlphabet[int(b)%len(randomIDAlphabet)])
147+
acceptedThisRead++
144148
if int64(len(chars)) == length {
145149
break
146150
}
147151
}
152+
if acceptedThisRead == 0 {
153+
stalledReads++
154+
if stalledReads > maxRandomIDStallReads {
155+
return NewNil(), fmt.Errorf("random_id entropy source rejected too many bytes")
156+
}
157+
continue
158+
}
159+
stalledReads = 0
148160
}
149161
return NewString(string(chars)), nil
150162
}

vibes/runtime_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,6 +2254,23 @@ func TestRandomIdentifierBuiltinsUsesUnbiasedSampling(t *testing.T) {
22542254
}
22552255
}
22562256

2257+
func TestRandomIdentifierBuiltinsRejectsStalledEntropy(t *testing.T) {
2258+
engine := MustNewEngine(Config{RandomReader: bytes.NewReader(bytes.Repeat([]byte{0xFF}, 1024))})
2259+
script, err := engine.Compile(`
2260+
def run()
2261+
random_id(4)
2262+
end
2263+
`)
2264+
if err != nil {
2265+
t.Fatalf("compile error: %v", err)
2266+
}
2267+
2268+
_, err = script.Call(context.Background(), "run", nil, CallOptions{})
2269+
if err == nil || !strings.Contains(err.Error(), "random_id entropy source rejected too many bytes") {
2270+
t.Fatalf("expected stalled entropy error, got %v", err)
2271+
}
2272+
}
2273+
22572274
func TestNumericHelpers(t *testing.T) {
22582275
script := compileScript(t, `
22592276
def int_helpers()

0 commit comments

Comments
 (0)