mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 05:40:11 +01:00
feat: Add function to find product identification helpers inspecting the tree (#505)
* feat: Add function to find product identification helpers inspecting the tree Signed-off-by: juan131 <jariza@vmware.com> * fix: simplify unit tests Signed-off-by: juan131 <jariza@vmware.com> * fix: also iterate over relationships Signed-off-by: juan131 <jariza@vmware.com> * fix: adapt example to use new library function Signed-off-by: juan131 <jariza@vmware.com> * Separate collecting and visiting of the product id helpers. --------- Signed-off-by: juan131 <jariza@vmware.com> Co-authored-by: Sascha L. Teichmann <sascha.teichmann@intevation.de>
This commit is contained in:
parent
b457dc872f
commit
9073a8a282
3 changed files with 258 additions and 96 deletions
61
csaf/util.go
61
csaf/util.go
|
|
@ -36,3 +36,64 @@ func ExtractProviderURL(r io.Reader, all bool) ([]string, error) {
|
|||
}
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
// CollectProductIdentificationHelpers returns a slice of all ProductIdentificationHelper
|
||||
// for a given ProductID.
|
||||
func (pt *ProductTree) CollectProductIdentificationHelpers(id ProductID) []*ProductIdentificationHelper {
|
||||
var helpers []*ProductIdentificationHelper
|
||||
pt.FindProductIdentificationHelpers(
|
||||
id, func(helper *ProductIdentificationHelper) {
|
||||
helpers = append(helpers, helper)
|
||||
})
|
||||
return helpers
|
||||
}
|
||||
|
||||
// FindProductIdentificationHelpers calls visit on all ProductIdentificationHelper
|
||||
// for a given ProductID by iterating over all full product names and branches
|
||||
// recursively available in the ProductTree.
|
||||
func (pt *ProductTree) FindProductIdentificationHelpers(
|
||||
id ProductID,
|
||||
visit func(*ProductIdentificationHelper),
|
||||
) {
|
||||
// Iterate over all full product names
|
||||
if fpns := pt.FullProductNames; fpns != nil {
|
||||
for _, fpn := range *fpns {
|
||||
if fpn != nil &&
|
||||
fpn.ProductID != nil && *fpn.ProductID == id &&
|
||||
fpn.ProductIdentificationHelper != nil {
|
||||
visit(fpn.ProductIdentificationHelper)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over branches recursively
|
||||
var recBranch func(b *Branch)
|
||||
recBranch = func(b *Branch) {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
if fpn := b.Product; fpn != nil &&
|
||||
fpn.ProductID != nil && *fpn.ProductID == id &&
|
||||
fpn.ProductIdentificationHelper != nil {
|
||||
visit(fpn.ProductIdentificationHelper)
|
||||
}
|
||||
for _, c := range b.Branches {
|
||||
recBranch(c)
|
||||
}
|
||||
}
|
||||
for _, b := range pt.Branches {
|
||||
recBranch(b)
|
||||
}
|
||||
|
||||
// Iterate over relationships
|
||||
if rels := pt.RelationShips; rels != nil {
|
||||
for _, rel := range *rels {
|
||||
if rel != nil {
|
||||
if fpn := rel.FullProductName; fpn != nil && fpn.ProductID != nil &&
|
||||
*fpn.ProductID == id && fpn.ProductIdentificationHelper != nil {
|
||||
visit(fpn.ProductIdentificationHelper)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
182
csaf/util_test.go
Normal file
182
csaf/util_test.go
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// This file is Free Software under the MIT License
|
||||
// without warranty, see README.md and LICENSES/MIT.txt for details.
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
|
||||
// Software-Engineering: 2022 Intevation GmbH <https://intevation.de>
|
||||
|
||||
package csaf
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProductTree_FindProductIdentificationHelpers(t *testing.T) {
|
||||
type fields struct {
|
||||
Branches Branches
|
||||
FullProductNames *FullProductNames
|
||||
RelationShips *Relationships
|
||||
}
|
||||
type args struct {
|
||||
id ProductID
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []*ProductIdentificationHelper
|
||||
}{
|
||||
{
|
||||
name: "empty product tree",
|
||||
args: args{
|
||||
id: "CSAFPID-0001",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "product tree with matching full product names",
|
||||
fields: fields{
|
||||
FullProductNames: &FullProductNames{{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0001",
|
||||
},
|
||||
want: []*ProductIdentificationHelper{{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "product tree with no matching full product names",
|
||||
fields: fields{
|
||||
FullProductNames: &FullProductNames{{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0002",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "product tree with matching branches",
|
||||
fields: fields{
|
||||
Branches: Branches{{
|
||||
Name: &[]string{"beta"}[0],
|
||||
Product: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
Branches: Branches{{
|
||||
Name: &[]string{"beta-2"}[0],
|
||||
Product: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta-2:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0001",
|
||||
},
|
||||
want: []*ProductIdentificationHelper{{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
}, {
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta-2:*:*:*:*:*:*"}[0],
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "product tree with no matching branches",
|
||||
fields: fields{
|
||||
Branches: Branches{{
|
||||
Name: &[]string{"beta"}[0],
|
||||
Product: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
Branches: Branches{{
|
||||
Name: &[]string{"beta-2"}[0],
|
||||
Product: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta-2:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0002",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "product tree with matching relationships",
|
||||
fields: fields{
|
||||
RelationShips: &Relationships{{
|
||||
FullProductName: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0001",
|
||||
},
|
||||
want: []*ProductIdentificationHelper{{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "product tree with no matching relationships",
|
||||
fields: fields{
|
||||
RelationShips: &Relationships{{
|
||||
FullProductName: &FullProductName{
|
||||
ProductID: &[]ProductID{"CSAFPID-0001"}[0],
|
||||
ProductIdentificationHelper: &ProductIdentificationHelper{
|
||||
CPE: &[]CPE{"cpe:2.3:a:microsoft:internet_explorer:1.0.0:beta:*:*:*:*:*:*"}[0],
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
id: "CSAFPID-0002",
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
for _, testToRun := range tests {
|
||||
test := testToRun
|
||||
t.Run(test.name, func(tt *testing.T) {
|
||||
tt.Parallel()
|
||||
pt := &ProductTree{
|
||||
Branches: test.fields.Branches,
|
||||
FullProductNames: test.fields.FullProductNames,
|
||||
RelationShips: test.fields.RelationShips,
|
||||
}
|
||||
if got := pt.CollectProductIdentificationHelpers(test.args.id); !reflect.DeepEqual(got, test.want) {
|
||||
tt.Errorf("ProductTree.FindProductIdentificationHelpers() = %v, want %v",
|
||||
got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue