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) {