fix: prevent incorrect binding

This commit is contained in:
Brendan Le Glaunec
2026-01-28 20:25:33 +01:00
parent 77a2eac262
commit a867a606b2
2 changed files with 43 additions and 4 deletions
+31 -4
View File
@@ -60,7 +60,7 @@ func findChannelIncrement(route string) (incrementalRoute, bool) {
}
pos += index
start, end, ok := firstNumberAfter(route, pos+len(pattern))
start, end, ok := firstNumberAfterKey(route, pos+len(pattern))
if ok {
num, width, parseOK := parseNumber(route, start, end)
if parseOK {
@@ -131,23 +131,41 @@ func parseNumber(route string, start, end int) (int, int, bool) {
return num, len(value), true
}
// firstNumberAfter returns the first numeric token after a given index.
func firstNumberAfter(route string, after int) (start, end int, ok bool) {
// firstNumberAfterKey returns the first numeric token after a keyword, limited to
// the current token and requiring an '=' delimiter (query param or path segment).
func firstNumberAfterKey(route string, after int) (start, end int, ok bool) {
if after < 0 {
after = 0
}
tokenEnd := len(route)
for i := after; i < len(route); i++ {
if isTokenDelimiter(route[i]) {
tokenEnd = i
break
}
}
relEq := strings.IndexByte(route[after:tokenEnd], '=')
searchStart := after
if relEq != -1 {
searchStart = after + relEq + 1
}
for i := searchStart; i < tokenEnd; i++ {
if !isDigit(route[i]) {
if relEq == -1 {
break
}
continue
}
end := i + 1
for end < len(route) && isDigit(route[end]) {
for end < tokenEnd && isDigit(route[end]) {
end++
}
return i, end, true
}
return 0, 0, false
}
@@ -163,3 +181,12 @@ func buildIncrementedRoute(match incrementalRoute, number int) string {
func isDigit(b byte) bool {
return b >= '0' && b <= '9'
}
func isTokenDelimiter(b byte) bool {
switch b {
case '&', '/', '?', '#':
return true
default:
return false
}
}
+12
View File
@@ -66,6 +66,18 @@ func TestDetectIncrementalRoute_OverflowAtEndFallsBack(t *testing.T) {
assert.Equal(t, "/bar999999999999999999999999999999", match.suffix)
}
func TestDetectIncrementalRoute_ChannelKeywordShouldNotBindAcrossParams(t *testing.T) {
// The channel keyword should not bind to digits in other query parameters.
route := "/path?channelname=foo&version=12"
match, ok := detectIncrementalRoute(route)
require.True(t, ok)
assert.False(t, match.isChannel)
assert.Equal(t, 12, match.number)
assert.Equal(t, "/path?channelname=foo&version=", match.prefix)
assert.Equal(t, "", match.suffix)
}
func TestBuildIncrementedRoute_ZeroPadding(t *testing.T) {
match := incrementalRoute{
prefix: "/channel",