Skip to content

Commit 3ab44d8

Browse files
committed
Add input validation for NetworksWithin API
Add validation for invalid prefixes in NetworksWithin to prevent unexpected behavior with malformed input. Invalid prefixes now return proper errors instead of potentially causing undefined behavior. Includes test coverage demonstrating proper error handling patterns for invalid prefix scenarios.
1 parent 8059668 commit 3ab44d8

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changes
22

3+
## 2.0.0-beta.9
4+
5+
- **SECURITY**: Fixed integer overflow vulnerability in search tree size
6+
calculation that could potentially allow malformed databases to trigger
7+
security issues.
8+
- **SECURITY**: Enhanced bounds checking in tree traversal functions to return
9+
proper errors instead of silent failures when encountering malformed
10+
databases.
11+
- Added validation for invalid prefixes in `NetworksWithin` to prevent
12+
unexpected behavior with malformed input.
13+
314
## 2.0.0-beta.8 - 2025-07-15
415

516
- Fixed "no next offset available" error that occurred when using custom

reader_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,3 +1321,26 @@ func TestIntegerOverflowProtection(t *testing.T) {
13211321
// This tests the condition: if recordSizeQuarter > 0
13221322
})
13231323
}
1324+
1325+
func TestNetworksWithinInvalidPrefix(t *testing.T) {
1326+
reader, err := Open(testFile("GeoIP2-Country-Test.mmdb"))
1327+
require.NoError(t, err)
1328+
defer func() {
1329+
require.NoError(t, reader.Close())
1330+
}()
1331+
1332+
// Test what happens when user ignores ParsePrefix error and passes invalid prefix
1333+
var invalidPrefix netip.Prefix // Zero value - invalid prefix
1334+
1335+
foundError := false
1336+
for result := range reader.NetworksWithin(invalidPrefix) {
1337+
if result.Err() != nil {
1338+
foundError = true
1339+
// Check that we get an appropriate error message
1340+
assert.Contains(t, result.Err().Error(), "invalid prefix")
1341+
break
1342+
}
1343+
}
1344+
1345+
assert.True(t, foundError, "Expected error when using invalid prefix")
1346+
}

traverse.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package maxminddb
22

33
import (
4+
"errors"
45
"fmt"
56
// comment to prevent gofumpt from randomly moving iter.
67
"iter"
@@ -78,6 +79,12 @@ func (r *Reader) Networks(options ...NetworksOption) iter.Seq[Result] {
7879
// [IncludeNetworksWithoutData].
7980
func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption) iter.Seq[Result] {
8081
return func(yield func(Result) bool) {
82+
if !prefix.IsValid() {
83+
yield(Result{
84+
err: errors.New("invalid prefix"),
85+
})
86+
return
87+
}
8188
if r.Metadata.IPVersion == 4 && prefix.Addr().Is6() {
8289
yield(Result{
8390
err: fmt.Errorf(
@@ -101,6 +108,13 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
101108
stopBit += 96
102109
}
103110

111+
if stopBit > 128 {
112+
yield(Result{
113+
err: errors.New("invalid prefix: exceeds IPv6 maximum of 128 bits"),
114+
})
115+
return
116+
}
117+
104118
pointer, bit, err := r.traverseTree(ip, 0, stopBit)
105119
if err != nil {
106120
yield(Result{
@@ -189,7 +203,15 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
189203
ipRight[node.bit>>3] |= 1 << (7 - (node.bit % 8))
190204

191205
offset := node.pointer * r.nodeOffsetMult
192-
rightPointer := readNodeBySize(r.buffer, offset, 1, r.Metadata.RecordSize)
206+
rightPointer, err := readNodeBySize(r.buffer, offset, 1, r.Metadata.RecordSize)
207+
if err != nil {
208+
yield(Result{
209+
ip: mappedIP(node.ip),
210+
prefixLen: uint8(node.bit),
211+
err: err,
212+
})
213+
return
214+
}
193215

194216
node.bit++
195217
nodes = append(nodes, netNode{
@@ -198,7 +220,16 @@ func (r *Reader) NetworksWithin(prefix netip.Prefix, options ...NetworksOption)
198220
bit: node.bit,
199221
})
200222

201-
node.pointer = readNodeBySize(r.buffer, offset, 0, r.Metadata.RecordSize)
223+
leftPointer, err := readNodeBySize(r.buffer, offset, 0, r.Metadata.RecordSize)
224+
if err != nil {
225+
yield(Result{
226+
ip: mappedIP(node.ip),
227+
prefixLen: uint8(node.bit),
228+
err: err,
229+
})
230+
return
231+
}
232+
node.pointer = leftPointer
202233
}
203234
}
204235
}

0 commit comments

Comments
 (0)