fix(06-12): reject typed-nil guards and fractional JWT subjects

This commit is contained in:
Jakub Zych
2026-09-21 19:43:35 +02:00
parent 4ad2ad29f2
commit 8a9449df63
4 changed files with 38 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"net/http"
"strconv"
"strings"
@@ -148,12 +149,16 @@ func subject(claims jwt.MapClaims) string {
case string:
return strings.TrimSpace(v)
case float64:
if v <= 0 {
if math.IsNaN(v) || math.IsInf(v, 0) || v <= 0 || v != math.Trunc(v) || v > 9007199254740992 {
return ""
}
return strconv.FormatInt(int64(v), 10)
case json.Number:
return strings.TrimSpace(v.String())
n, err := strconv.ParseInt(strings.TrimSpace(v.String()), 10, 64)
if err != nil || n <= 0 {
return ""
}
return strconv.FormatInt(n, 10)
default:
return ""
}

View File

@@ -240,3 +240,15 @@ func noneToken(t *testing.T, claims jwt.MapClaims) string {
body := base64.RawURLEncoding.EncodeToString(payload)
return header + "." + body + "."
}
func TestVerifyRejectsFractionalSubject(t *testing.T) {
exp := time.Now().Add(time.Hour).Unix()
tok := sign(t, jwt.SigningMethodHS256, jwt.MapClaims{"sub": 12.5, "exp": exp}, []byte(secret))
if sub, err := Verify(tok, secret); err == nil || sub != "" {
t.Fatalf("fractional sub accepted: %q %v", sub, err)
}
tok = sign(t, jwt.SigningMethodHS256, jwt.MapClaims{"sub": 12, "exp": exp}, []byte(secret))
if sub, err := Verify(tok, secret); err != nil || sub != "12" {
t.Fatalf("whole sub: %q %v", sub, err)
}
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"net/http"
"reflect"
)
type namedGuard struct {
@@ -32,6 +33,12 @@ func (reg *Registry) Register(pluginID, name string, g any) error {
if name == "" || g == nil {
return fmt.Errorf("bouncer: plugin %q registered empty guard %q", pluginID, name)
}
switch rv := reflect.ValueOf(g); rv.Kind() {
case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Func, reflect.Chan, reflect.Interface:
if rv.IsNil() {
return fmt.Errorf("bouncer: plugin %q registered empty guard %q", pluginID, name)
}
}
_, isGuard := g.(Guard)
_, isCred := g.(CredentialGuard)
if !isGuard && !isCred {

View File

@@ -233,3 +233,15 @@ func TestJWTGuardRegistryMatchesMiddleware(t *testing.T) {
})
}
}
func TestRegisterRejectsTypedNilGuard(t *testing.T) {
var reg Registry
var g *credPtrGuard
if err := reg.Register("p", "n", g); err == nil {
t.Fatal("typed-nil guard accepted")
}
}
type credPtrGuard struct{}
func (*credPtrGuard) Authenticate(*http.Request) (*Principal, error) { return nil, nil }