Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions builtin/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,10 @@ func get(params ...any) (out any, err error) {

case reflect.Struct:
fieldName := i.(string)
value := v.FieldByNameFunc(func(name string) bool {
field, _ := v.Type().FieldByName(name)
switch field.Tag.Get("expr") {
t := v.Type()
field, ok := t.FieldByNameFunc(func(name string) bool {
f, _ := t.FieldByName(name)
switch f.Tag.Get("expr") {
case "-":
return false
case fieldName:
Expand All @@ -607,8 +608,11 @@ func get(params ...any) (out any, err error) {
return name == fieldName
}
})
if value.IsValid() {
return value.Interface(), nil
if ok && field.IsExported() {
value := v.FieldByIndex(field.Index)
if value.IsValid() {
return value.Interface(), nil
}
}
}

Expand Down
53 changes: 53 additions & 0 deletions test/issues/934/issue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package issue934

import (
"testing"

"github.com/expr-lang/expr"
"github.com/expr-lang/expr/internal/testify/require"
)

type Env struct {
Exported
unexported
}

type Exported struct {
Str string
str string
}

type unexported struct {
Integer int
integer int
}

// TestIssue934 tests that accessing unexported fields on values whose type
// is unknown at compile time (e.g., from a ternary with mixed types) yields
// a descriptive error.
//
// OSS-Fuzz issue #486370271.
func TestIssue934(t *testing.T) {
env := map[string]any{
"v": Env{},
}

// Accessing unexported fields like time.Time.loc
// should produce a proper error.
_, err := expr.Eval(`(true ? v : "string").str`, env)
require.Error(t, err)
require.Contains(t, err.Error(), "cannot fetch str")

// Exported field access should still work.
_, err = expr.Eval(`(true ? v : "string").Str`, env)
require.NoError(t, err)

// Access unexported field inherited from unexported embedded struct.
_, err = expr.Eval(`(true ? v : "string").integer`, env)
require.Error(t, err)
require.Contains(t, err.Error(), "cannot fetch integer")

// Access exported field inherited from unexported embedded struct should work.
_, err = expr.Eval(`(true ? v : "string").Integer`, env)
require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion vm/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func Fetch(from, i any) any {
return name == fieldName
}
})
if ok {
if ok && field.IsExported() {
value := v.FieldByIndex(field.Index)
if value.IsValid() {
fieldCache.Store(key, field.Index)
Expand Down
Loading