Skip to content

Commit 8761791

Browse files
authored
fix(kyaml/yaml): minor nil safety fix for RNode.Content etc (#5985)
* Fix kyaml/yaml field access deref nil value for methods that look "nil-safe" This change is addressing observed panics within kustomize that obscure the actual failure. The primary observed problem case involves RNode.Content. * Fix test case * Fixes from review
1 parent 153a372 commit 8761791

File tree

2 files changed

+92
-6
lines changed

2 files changed

+92
-6
lines changed

kyaml/yaml/rnode.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -718,10 +718,11 @@ func (rn *RNode) MustString() string {
718718

719719
// Content returns Node Content field.
720720
func (rn *RNode) Content() []*yaml.Node {
721-
if rn == nil {
721+
yNode := rn.YNode()
722+
if yNode == nil {
722723
return nil
723724
}
724-
return rn.YNode().Content
725+
return yNode.Content
725726
}
726727

727728
// Fields returns the list of field names for a MappingNode.
@@ -756,7 +757,11 @@ func (rn *RNode) FieldRNodes() ([]*RNode, error) {
756757
// Field returns a fieldName, fieldValue pair for MappingNodes.
757758
// Returns nil for non-MappingNodes.
758759
func (rn *RNode) Field(field string) *MapNode {
759-
if rn.YNode().Kind != yaml.MappingNode {
760+
yNode := rn.YNode()
761+
if yNode == nil {
762+
return nil
763+
}
764+
if yNode.Kind != yaml.MappingNode {
760765
return nil
761766
}
762767
var result *MapNode
@@ -892,7 +897,11 @@ func (rn *RNode) ElementValuesList(keys []string) ([][]string, error) {
892897
// Element returns the element in the list which contains the field matching the value.
893898
// Returns nil for non-SequenceNodes or if no Element matches.
894899
func (rn *RNode) Element(key, value string) *RNode {
895-
if rn.YNode().Kind != yaml.SequenceNode {
900+
yNode := rn.YNode()
901+
if yNode == nil {
902+
return nil
903+
}
904+
if yNode.Kind != yaml.SequenceNode {
896905
return nil
897906
}
898907
elem, err := rn.Pipe(MatchElement(key, value))
@@ -906,7 +915,11 @@ func (rn *RNode) Element(key, value string) *RNode {
906915
// corresponding values[i].
907916
// Returns nil for non-SequenceNodes or if no Element matches.
908917
func (rn *RNode) ElementList(keys []string, values []string) *RNode {
909-
if rn.YNode().Kind != yaml.SequenceNode {
918+
yNode := rn.YNode()
919+
if yNode == nil {
920+
return nil
921+
}
922+
if yNode.Kind != yaml.SequenceNode {
910923
return nil
911924
}
912925
elem, err := rn.Pipe(MatchElementList(keys, values))
@@ -960,12 +973,17 @@ func (rn *RNode) GetAssociativeKey() string {
960973

961974
// MarshalJSON creates a byte slice from the RNode.
962975
func (rn *RNode) MarshalJSON() ([]byte, error) {
976+
yNode := rn.YNode()
977+
if yNode == nil {
978+
return []byte("null"), nil
979+
}
980+
963981
s, err := rn.String()
964982
if err != nil {
965983
return nil, err
966984
}
967985

968-
if rn.YNode().Kind == SequenceNode {
986+
if yNode.Kind == SequenceNode {
969987
var a []interface{}
970988
if err := Unmarshal([]byte(s), &a); err != nil {
971989
return nil, err
@@ -977,6 +995,7 @@ func (rn *RNode) MarshalJSON() ([]byte, error) {
977995
if err := Unmarshal([]byte(s), &m); err != nil {
978996
return nil, err
979997
}
998+
980999
return json.Marshal(m)
9811000
}
9821001

kyaml/yaml/rnode_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,3 +2356,70 @@ metadata:
23562356
require.NoError(t, err)
23572357
require.Equal(t, "hello-world-foo", fooAppName) // no field named 'foo.appname'
23582358
}
2359+
2360+
func TestRNode_nilSafety(t *testing.T) {
2361+
// Both of these scenarios should cause rn.YNode() to return nil.
2362+
nodesToTest := [...]struct {
2363+
name string
2364+
rn *RNode
2365+
}{
2366+
{"nil *RNode receiver", nil},
2367+
{"RNode with nil internal node", &RNode{value: nil}},
2368+
}
2369+
2370+
t.Run("Content", func(t *testing.T) {
2371+
for _, tc := range nodesToTest {
2372+
t.Run(tc.name, func(t *testing.T) {
2373+
assert.NotPanics(t, func() {
2374+
assert.Nil(t, tc.rn.Content())
2375+
})
2376+
})
2377+
}
2378+
})
2379+
2380+
t.Run("Field", func(t *testing.T) {
2381+
for _, tc := range nodesToTest {
2382+
t.Run(tc.name, func(t *testing.T) {
2383+
assert.NotPanics(t, func() {
2384+
assert.Nil(t, tc.rn.Field("any-field"))
2385+
})
2386+
})
2387+
}
2388+
})
2389+
2390+
t.Run("Element", func(t *testing.T) {
2391+
for _, tc := range nodesToTest {
2392+
t.Run(tc.name, func(t *testing.T) {
2393+
assert.NotPanics(t, func() {
2394+
assert.Nil(t, tc.rn.Element("any-key", "any-value"))
2395+
})
2396+
})
2397+
}
2398+
})
2399+
2400+
t.Run("ElementList", func(t *testing.T) {
2401+
for _, tc := range nodesToTest {
2402+
t.Run(tc.name, func(t *testing.T) {
2403+
assert.NotPanics(t, func() {
2404+
assert.Nil(t, tc.rn.ElementList([]string{"any-key"}, []string{"any-value"}))
2405+
})
2406+
})
2407+
}
2408+
})
2409+
2410+
t.Run("MarshalJSON", func(t *testing.T) {
2411+
for _, tc := range nodesToTest {
2412+
t.Run(tc.name, func(t *testing.T) {
2413+
var (
2414+
jsonData []byte
2415+
err error
2416+
)
2417+
assert.NotPanics(t, func() {
2418+
jsonData, err = tc.rn.MarshalJSON()
2419+
})
2420+
require.NoError(t, err)
2421+
assert.Equal(t, []byte("null"), jsonData)
2422+
})
2423+
}
2424+
})
2425+
}

0 commit comments

Comments
 (0)