This is an automated email from the ASF dual-hosted git repository.

laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 16515245f fix(expr): reject trailing data after boolean expressions 
(#1609)
16515245f is described below

commit 16515245fc759fc3259f35357aedd09eb39e06cd
Author: Minh Vu <[email protected]>
AuthorDate: Tue Aug 4 12:18:12 2026 +0200

    fix(expr): reject trailing data after boolean expressions (#1609)
    
    ## What changed
    
    Fully decode top-level boolean expressions before returning `AlwaysTrue`
    or `AlwaysFalse`. This rejects malformed input containing a valid
    boolean followed by additional JSON or other trailing data.
    
    ## Why
    
    The expression decoder previously inspected only the first token for
    booleans. Inputs such as `true false` were therefore accepted even
    though they are not valid JSON values. Object expressions already reject
    trailing data through `json.Unmarshal`.
    
    ## Testing
    
    - `go test ./...`
    
    ---------
    
    Signed-off-by: Minh Vu <[email protected]>
---
 expr_json.go      | 17 ++++++++++++++++-
 expr_json_test.go | 14 ++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/expr_json.go b/expr_json.go
index a81b5f4bd..91cd8ed4e 100644
--- a/expr_json.go
+++ b/expr_json.go
@@ -21,7 +21,9 @@ import (
        "bytes"
        "encoding/hex"
        "encoding/json"
+       "errors"
        "fmt"
+       "io"
        "math"
        "sort"
        "strings"
@@ -488,8 +490,21 @@ func decodeExpr(raw json.RawMessage, schema *Schema, 
caseSensitive bool) (Boolea
 // without inspecting bytes by hand.
 func firstToken(raw json.RawMessage) (json.Token, error) {
        dec := json.NewDecoder(bytes.NewReader(raw))
+       tok, err := dec.Token()
+       if err != nil {
+               return nil, err
+       }
+       if _, ok := tok.(bool); ok {
+               if _, err := dec.Token(); !errors.Is(err, io.EOF) {
+                       if err == nil {
+                               return nil, errors.New("trailing data after 
boolean expression")
+                       }
+
+                       return nil, err
+               }
+       }
 
-       return dec.Token()
+       return tok, nil
 }
 
 func decodePredicate(op Operation, node exprNode, schema *Schema, 
caseSensitive bool) (BooleanExpression, error) {
diff --git a/expr_json_test.go b/expr_json_test.go
index b5841a4b8..1b9de31d6 100644
--- a/expr_json_test.go
+++ b/expr_json_test.go
@@ -393,6 +393,20 @@ func TestUnmarshalExpressionErrors(t *testing.T) {
        }
 }
 
+func TestUnmarshalBooleanExpressionRejectsTrailingData(t *testing.T) {
+       for _, input := range []string{
+               `true false`,
+               `false null`,
+               `true{}`,
+               `false garbage`,
+       } {
+               t.Run(input, func(t *testing.T) {
+                       _, err := iceberg.ParseExpr([]byte(input), nil)
+                       require.ErrorIs(t, err, iceberg.ErrInvalidArgument)
+               })
+       }
+}
+
 // TestExpressionTransformTermRoundTrip covers a residual filter whose term is 
a
 // partition transform, e.g. a Java server's bucket[100](id) <= 50.
 func TestExpressionTransformTermRoundTrip(t *testing.T) {

Reply via email to