From 1adc031f7509f7872f48456be5e5e0fecd4b2320 Mon Sep 17 00:00:00 2001 From: Quint Daenen Date: Thu, 26 Mar 2026 13:17:46 +0100 Subject: [PATCH] fix: reject ordering operators on boolean/binary attributes during validation Validate() only checked attribute path existence, not type-operator compatibility. Filters like `active gt true` passed validation despite RFC 7644 Section 3.4.2.2 requiring a 400 response for boolean/binary attributes with gt, lt, ge, or le operators. --- filter/filter.go | 25 ++++++++++++++++++++++++- filter/filter_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/filter/filter.go b/filter/filter.go index fc12719..a103a74 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -48,7 +48,17 @@ func validateExpression(ref schema.Schema, e filter.Expression) error { } return nil case *filter.AttributeExpression: - if _, err := validateAttributePath(ref, e.AttributePath); err != nil { + attr, err := validateAttributePath(ref, e.AttributePath) + if err != nil { + return err + } + resolved := attr + if sub := e.AttributePath.SubAttributeName(); sub != "" { + if a, ok := attr.SubAttributes().ContainsAttribute(sub); ok { + resolved = a + } + } + if err := validateOperator(resolved, e.Operator); err != nil { return err } return nil @@ -70,6 +80,19 @@ func validateExpression(ref schema.Schema, e filter.Expression) error { } } +// validateOperator checks whether the given operator is compatible with the attribute type. +// Per RFC 7644 Section 3.4.2.2, boolean and binary attributes do not support gt, lt, ge, or le. +func validateOperator(attr schema.CoreAttribute, op filter.CompareOperator) error { + switch attr.AttributeType() { + case "boolean", "binary": + switch op { + case filter.GT, filter.LT, filter.GE, filter.LE: + return fmt.Errorf("operator %q is not supported for %s attributes", op, attr.AttributeType()) + } + } + return nil +} + // validateSubAttribute checks whether the given attribute name is a attribute within the given reference attribute. func validateSubAttribute(attr schema.CoreAttribute, subAttrName string) error { if !attr.HasSubAttributes() { diff --git a/filter/filter_test.go b/filter/filter_test.go index 62293f6..8bcab4a 100644 --- a/filter/filter_test.go +++ b/filter/filter_test.go @@ -211,6 +211,7 @@ func TestValidator_Validate(t *testing.T) { `userType eq "Employee" and (emails.type eq "work")`, `userType eq "Employee" and emails[type eq "work" and value co "@example.com"]`, `emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]`, + `active eq true`, } { validator, err := filter.NewValidator(f, userSchema) if err != nil { @@ -222,6 +223,40 @@ func TestValidator_Validate(t *testing.T) { } } +func TestValidator_Validate_orderingOperatorOnBinaryAttribute(t *testing.T) { + ref := schema.Schema{ + Attributes: []schema.CoreAttribute{ + schema.SimpleCoreAttribute(schema.SimpleBinaryParams(schema.BinaryParams{ + Name: "bin", + })), + }, + } + for _, op := range []string{"gt", "lt", "ge", "le"} { + f := `bin ` + op + ` "aGVsbG8="` + validator, err := filter.NewValidator(f, ref) + if err != nil { + t.Fatal(err) + } + if err := validator.Validate(); err == nil { + t.Errorf("expected error for %q on binary attribute, got nil", f) + } + } +} + +func TestValidator_Validate_orderingOperatorOnBooleanAttribute(t *testing.T) { + userSchema := schema.CoreUserSchema() + for _, op := range []string{"gt", "lt", "ge", "le"} { + f := "active " + op + " true" + validator, err := filter.NewValidator(f, userSchema) + if err != nil { + t.Fatal(err) + } + if err := validator.Validate(); err == nil { + t.Errorf("expected error for %q on boolean attribute, got nil", f) + } + } +} + func testResources() []map[string]interface{} { return []map[string]interface{}{ {