fix(06-12): reject typed-nil guards and fractional JWT subjects
This commit is contained in:
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user