+26
-2
@@ -416,15 +416,39 @@ func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Va
|
||||
|
||||
return v, nil
|
||||
case reflect.Slice:
|
||||
elem := v.Index(v.Len() - 1)
|
||||
// Create a new element when the slice is empty; otherwise operate on
|
||||
// the last element.
|
||||
var (
|
||||
elem reflect.Value
|
||||
created bool
|
||||
)
|
||||
if v.Len() == 0 {
|
||||
created = true
|
||||
elemType := v.Type().Elem()
|
||||
if elemType.Kind() == reflect.Interface {
|
||||
elem = makeMapStringInterface()
|
||||
} else {
|
||||
elem = reflect.New(elemType).Elem()
|
||||
}
|
||||
} else {
|
||||
elem = v.Index(v.Len() - 1)
|
||||
}
|
||||
|
||||
x, err := d.handleArrayTable(key, elem)
|
||||
if err != nil || d.skipUntilTable {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
if x.IsValid() {
|
||||
elem.Set(x)
|
||||
if created {
|
||||
elem = x
|
||||
} else {
|
||||
elem.Set(x)
|
||||
}
|
||||
}
|
||||
|
||||
if created {
|
||||
return reflect.Append(v, elem), nil
|
||||
}
|
||||
return v, err
|
||||
case reflect.Array:
|
||||
idx := d.arrayIndex(false, v)
|
||||
|
||||
+203
-4
@@ -412,7 +412,7 @@ foo = "bar"`,
|
||||
assert: func(t *testing.T, test test) {
|
||||
// Despite the documentation:
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
// assert.Equal does not work properly with maps with pointer keys
|
||||
// https://github.com/stretchr/testify/issues/1143
|
||||
expected := make(map[unmarshalTextKey]string)
|
||||
@@ -3884,9 +3884,9 @@ func TestUnmarshal_Nil(t *testing.T) {
|
||||
{
|
||||
desc: "simplest",
|
||||
input: `
|
||||
[foo]
|
||||
[foo.foo]
|
||||
`,
|
||||
[foo]
|
||||
[foo.foo]
|
||||
`,
|
||||
expected: "[foo]\n[foo.foo]\n",
|
||||
},
|
||||
}
|
||||
@@ -4040,3 +4040,202 @@ func TestIssue994_OK(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "bar from unmarshaler", d.S)
|
||||
}
|
||||
|
||||
func TestIssue995(t *testing.T) {
|
||||
type AllowList struct {
|
||||
Description string
|
||||
Condition string
|
||||
Commits []string
|
||||
Paths []string
|
||||
RegexTarget string
|
||||
Regexes []string
|
||||
StopWords []string
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
ID string
|
||||
Description string
|
||||
Regex string
|
||||
SecretGroup int
|
||||
Entropy interface{}
|
||||
Keywords []string
|
||||
Path string
|
||||
Tags []string
|
||||
AllowList *AllowList
|
||||
Allowlists []AllowList
|
||||
}
|
||||
|
||||
type GitleaksConfig struct {
|
||||
Description string
|
||||
Rules []Rule
|
||||
Allowlist struct {
|
||||
Commits []string
|
||||
Paths []string
|
||||
RegexTarget string
|
||||
Regexes []string
|
||||
StopWords []string
|
||||
}
|
||||
}
|
||||
|
||||
doc := `
|
||||
[[allowlists]]
|
||||
description = "Exception for File "
|
||||
files = [ '''app/src''']
|
||||
|
||||
[[rules.allowlists]]
|
||||
description = "policies"
|
||||
regexes = [
|
||||
'''abc'''
|
||||
]
|
||||
`
|
||||
|
||||
var cfg GitleaksConfig
|
||||
err := toml.Unmarshal([]byte(doc), &cfg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Ensure no panic and that nested array table was created.
|
||||
if len(cfg.Rules) == 0 {
|
||||
t.Fatalf("expected Rules to contain at least one element after unmarshaling nested array table")
|
||||
}
|
||||
if len(cfg.Rules[0].Allowlists) != 1 {
|
||||
t.Fatalf("expected first Rule to have exactly one allowlists entry, got %d", len(cfg.Rules[0].Allowlists))
|
||||
}
|
||||
assert.Equal(t, "policies", cfg.Rules[0].Allowlists[0].Description)
|
||||
assert.Equal(t, []string{"abc"}, cfg.Rules[0].Allowlists[0].Regexes)
|
||||
}
|
||||
|
||||
func TestIssue995_InterfaceSlice_MultiNested(t *testing.T) {
|
||||
type Root struct {
|
||||
Rules []interface{}
|
||||
}
|
||||
|
||||
doc := `
|
||||
[[rules.allowlists]]
|
||||
description = "a"
|
||||
|
||||
[[rules.allowlists]]
|
||||
description = "b"
|
||||
`
|
||||
|
||||
var r Root
|
||||
err := toml.Unmarshal([]byte(doc), &r)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(r.Rules) != 1 {
|
||||
t.Fatalf("expected one element in Rules, got %d", len(r.Rules))
|
||||
}
|
||||
|
||||
m, ok := r.Rules[0].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected Rules[0] to be a map[string]any, got %T", r.Rules[0])
|
||||
}
|
||||
|
||||
als, ok := m["allowlists"].([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected allowlists to be []any, got %T", m["allowlists"])
|
||||
}
|
||||
if len(als) != 2 {
|
||||
t.Fatalf("expected 2 allowlists entries, got %d", len(als))
|
||||
}
|
||||
|
||||
a0, ok := als[0].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected allowlists[0] to be map[string]any, got %T", als[0])
|
||||
}
|
||||
a1, ok := als[1].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected allowlists[1] to be map[string]any, got %T", als[1])
|
||||
}
|
||||
assert.Equal(t, "a", a0["description"])
|
||||
assert.Equal(t, "b", a1["description"])
|
||||
}
|
||||
|
||||
func TestIssue995_MultiNestedConcrete(t *testing.T) {
|
||||
type AllowList struct {
|
||||
Description string
|
||||
}
|
||||
type Rule struct {
|
||||
Allowlists []AllowList
|
||||
}
|
||||
type Root struct {
|
||||
Rules []Rule
|
||||
}
|
||||
|
||||
doc := `
|
||||
[[rules.allowlists]]
|
||||
description = "a"
|
||||
|
||||
[[rules.allowlists]]
|
||||
description = "b"
|
||||
`
|
||||
|
||||
var r Root
|
||||
err := toml.Unmarshal([]byte(doc), &r)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(r.Rules) != 1 {
|
||||
t.Fatalf("expected one element in Rules, got %d", len(r.Rules))
|
||||
}
|
||||
assert.Equal(t, 2, len(r.Rules[0].Allowlists))
|
||||
assert.Equal(t, "a", r.Rules[0].Allowlists[0].Description)
|
||||
assert.Equal(t, "b", r.Rules[0].Allowlists[1].Description)
|
||||
}
|
||||
|
||||
func TestIssue995_PointerToSlice_Rules(t *testing.T) {
|
||||
type AllowList struct{ Description string }
|
||||
type Rule struct{ Allowlists []AllowList }
|
||||
type Root struct{ Rules *[]Rule }
|
||||
|
||||
doc := `
|
||||
[[rules.allowlists]]
|
||||
description = "a"
|
||||
|
||||
[[rules.allowlists]]
|
||||
description = "b"
|
||||
`
|
||||
|
||||
var r Root
|
||||
err := toml.Unmarshal([]byte(doc), &r)
|
||||
assert.NoError(t, err)
|
||||
if r.Rules == nil {
|
||||
t.Fatalf("expected Rules pointer to be initialized")
|
||||
}
|
||||
if len(*r.Rules) != 1 {
|
||||
t.Fatalf("expected one element in Rules, got %d", len(*r.Rules))
|
||||
}
|
||||
rule := (*r.Rules)[0]
|
||||
assert.Equal(t, 2, len(rule.Allowlists))
|
||||
assert.Equal(t, "a", rule.Allowlists[0].Description)
|
||||
assert.Equal(t, "b", rule.Allowlists[1].Description)
|
||||
}
|
||||
|
||||
func TestIssue995_SliceNonEmpty_UsesLastElement(t *testing.T) {
|
||||
type AllowList struct{ Description string }
|
||||
type Rule struct{ Allowlists []AllowList }
|
||||
type Root struct{ Rules []Rule }
|
||||
|
||||
// Pre-initialize with one Rule; nested array table should populate
|
||||
// the last element, not create a new one at this level.
|
||||
var r Root
|
||||
r.Rules = []Rule{{}}
|
||||
|
||||
doc := `
|
||||
[[rules.allowlists]]
|
||||
description = "a"
|
||||
|
||||
[[rules.allowlists]]
|
||||
description = "b"
|
||||
`
|
||||
|
||||
err := toml.Unmarshal([]byte(doc), &r)
|
||||
assert.NoError(t, err)
|
||||
if len(r.Rules) != 1 {
|
||||
t.Fatalf("expected one element in Rules, got %d", len(r.Rules))
|
||||
}
|
||||
assert.Equal(t, 2, len(r.Rules[0].Allowlists))
|
||||
// Values presence check
|
||||
got := []string{r.Rules[0].Allowlists[0].Description, r.Rules[0].Allowlists[1].Description}
|
||||
if !(got[0] == "a" && got[1] == "b") && !(got[0] == "b" && got[1] == "a") {
|
||||
t.Fatalf("unexpected values in allowlists: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user