From d08e2be768855d5e9c6e19d7e4f5e56642e4d936 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Fri, 25 Aug 2023 12:04:57 -0400 Subject: [PATCH] Fix panic in pom parsing (#2064) A recent update to gopom changed many fields to be omitted when empty, resulting in a number of nil pointers inside the nested structure of the pom that previously didn't exist. Defend against these in the search for the property value. Signed-off-by: Will Murphy --- syft/pkg/cataloger/java/parse_pom_xml.go | 14 ++++++++++++-- syft/pkg/cataloger/java/parse_pom_xml_test.go | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index f39bef1d636..a6f5340cf1f 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -188,7 +188,7 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s propertyCase := safeString(property) log.WithFields("existingPropertyValue", propertyCase, "propertyName", propertyName).Trace("resolving property") return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string { - propertyName := strings.TrimSpace(match[2 : len(match)-1]) + propertyName := strings.TrimSpace(match[2 : len(match)-1]) // remove leading ${ and trailing } entries := pomProperties(pom) if value, ok := entries[propertyName]; ok { return value @@ -210,16 +210,26 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s for fieldNum := 0; fieldNum < pomValueType.NumField(); fieldNum++ { f := pomValueType.Field(fieldNum) tag := f.Tag.Get("xml") - tag = strings.TrimSuffix(tag, ",omitempty") + tag = strings.Split(tag, ",")[0] + // a segment of the property name matches the xml tag for the field, + // so we need to recurse down the nested structs or return a match + // if we're done. if part == tag { pomValue = pomValue.Field(fieldNum) pomValueType = pomValue.Type() if pomValueType.Kind() == reflect.Ptr { + // we were recursing down the nested structs, but one of the steps + // we need to take is a nil pointer, so give up and return the original match + if pomValue.IsNil() { + return match + } pomValue = pomValue.Elem() if !pomValue.IsZero() { + // we found a non-zero value whose tag matches this part of the property name pomValueType = pomValue.Type() } } + // If this was the last part of the property name, return the value if partNum == numParts-1 { return fmt.Sprintf("%v", pomValue.Interface()) } diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index 98551452155..9e79fe96e84 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -446,6 +446,24 @@ func Test_resolveProperty(t *testing.T) { }, expected: "org.some.parent", }, + { + name: "nil pointer halts search", + property: "${project.parent.groupId}", + pom: gopom.Project{ + Parent: nil, + }, + expected: "${project.parent.groupId}", + }, + { + name: "nil string pointer halts search", + property: "${project.parent.groupId}", + pom: gopom.Project{ + Parent: &gopom.Parent{ + GroupID: nil, + }, + }, + expected: "${project.parent.groupId}", + }, } for _, test := range tests {