Skip to content

Commit

Permalink
Merge pull request #216 from felipecruz91/hotfix/multiple-products
Browse files Browse the repository at this point in the history
fix(create): support multiple `--product` flags
  • Loading branch information
puerco authored Jul 10, 2024
2 parents 8d81805 + c04df00 commit 01f6dfa
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 31 deletions.
4 changes: 2 additions & 2 deletions internal/cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ to preserve the original file, specify it using the --file flag:
}
opts.documentPath = args[i]
case 1:
if opts.Product != "" && opts.Product != args[i] {
if len(opts.Products) != 1 && len(args) != 1 {
return errors.New("product can only be specified once")
}
opts.Product = args[i]
opts.Products = append(opts.Products, args[i])
case 2:
if opts.Vulnerability != "" && opts.Vulnerability != args[i] {
return errors.New("vulnerability can only be specified once")
Expand Down
10 changes: 7 additions & 3 deletions internal/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,14 @@ Examples:
for i := range args {
switch i {
case 0:
if opts.Product != "" && opts.Product != args[i] {
return errors.New("product can only be specified once")
if len(opts.Products) > 0 && args[i] != "" {
return errors.New("multiple products can only be specified using the --product flag")
}
opts.Product = args[i]
// Specifying multiple products through args is not supported as we can't tell how many products are provided:
// e.g the second argument could be a vulnerability or a status instead of a product, for example.
// When using args only the first one is considered a product.
// To specify multiple products, use the --product flag multiple times instead.
opts.Products = append(opts.Products, args[i])
case 1:
if opts.Vulnerability != "" && opts.Vulnerability != args[i] {
return errors.New("vulnerability can only be specified once")
Expand Down
39 changes: 22 additions & 17 deletions internal/cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type vexStatementOptions struct {
ImpactStatement string
Vulnerability string
ActionStatement string
Product string
Products []string
Subcomponents []string
}

Expand All @@ -80,8 +80,12 @@ func (so *vexStatementOptions) Validate() error {
so.ActionStatement = ""
}

if so.Product == "" {
return errors.New("a required product id is needed to generate a valid VEX statement")
if len(so.Products) == 0 {
return errors.New("a minimum of one product id is needed to generate a valid VEX statement")
}

if len(so.Products) > 1 && len(so.Subcomponents) > 0 {
return errors.New("subcomponent(s) cannot be defined when specifying multiple products because it's unclear which subcomponent(s) belong to which products")
}

if so.Vulnerability == "" {
Expand Down Expand Up @@ -115,12 +119,12 @@ func (so *vexStatementOptions) AddFlags(cmd *cobra.Command) {
"vulnerability to add to the statement (eg CVE-2023-12345)",
)

cmd.PersistentFlags().StringVarP(
&so.Product,
cmd.PersistentFlags().StringSliceVarP(
&so.Products,
productLongFlag,
"p",
"",
"main identifier of the product, a package URL or another IRI",
[]string{},
"list of products to list in the statement, at least one is required (main identifier of the product, a package URL or another IRI)",
)

cmd.PersistentFlags().StringVarP(
Expand Down Expand Up @@ -177,16 +181,8 @@ func (so *vexStatementOptions) ToStatement() vex.Statement {
Vulnerability: vex.Vulnerability{
Name: vex.VulnerabilityID(so.Vulnerability),
},
Timestamp: &t,
LastUpdated: nil,
Products: []vex.Product{
{
Component: vex.Component{
ID: so.Product,
},
Subcomponents: []vex.Subcomponent{},
},
},
Timestamp: &t,
LastUpdated: nil,
Status: vex.Status(so.Status),
StatusNotes: so.StatusNotes,
Justification: vex.Justification(so.Justification),
Expand All @@ -199,6 +195,15 @@ func (so *vexStatementOptions) ToStatement() vex.Statement {
s.ActionStatementTimestamp = &t
}

for _, id := range so.Products {
s.Products = append(s.Products, vex.Product{
Component: vex.Component{
ID: id,
},
Subcomponents: []vex.Subcomponent{},
})
}

for _, sc := range so.Subcomponents {
s.Products[0].Subcomponents = append(s.Products[0].Subcomponents, vex.Subcomponent{
Component: vex.Component{ID: sc},
Expand Down
26 changes: 17 additions & 9 deletions internal/cmd/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,51 +54,59 @@ func TestVexStatementOptionsValidate(t *testing.T) {
},
"empty product": {
vexStatementOptions{
Status: string(vex.StatusUnderInvestigation),
Product: "",
Status: string(vex.StatusUnderInvestigation),
Products: []string{},
}, true,
},
"empty vulnerability": {
vexStatementOptions{
Status: string(vex.StatusUnderInvestigation),
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "",
}, true,
},
"empty status": {
vexStatementOptions{
Status: "",
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
}, true,
},
"invalid status": {
vexStatementOptions{
Status: "cheese",
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
}, true,
},
"justification on non-not-affected": {
vexStatementOptions{
Status: string(vex.StatusUnderInvestigation),
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
Justification: "justification goes here",
}, true,
},
"impact statement on non-not-affected": {
vexStatementOptions{
Status: string(vex.StatusUnderInvestigation),
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
ImpactStatement: "buffer underrun is never run under",
}, true,
},
"can't associate a subcomponent when multiple products are defined": {
vexStatementOptions{
Status: string(vex.StatusNotAffected),
Products: []string{"pkg:oci/alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", "pkg:oci/busybox@sha256:c6ab5a1a2bc330f3f9616b20c7fba7716cadd941514cf80f8d7c3da8af6b0946"},
Subcomponents: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
}, true,
},
"ok": {
vexStatementOptions{
Status: string(vex.StatusUnderInvestigation),
Product: "pkg:golang/fmt",
Products: []string{"pkg:golang/fmt"},
Vulnerability: "CVE-2014-12345678",
}, false,
},
Expand All @@ -114,7 +122,7 @@ func TestAddOptionsValidate(t *testing.T) {
stubOpts := vexStatementOptions{
Status: "fixed",
Vulnerability: "CVE-2014-1234678",
Product: "pkg:generic/test@1.00",
Products: []string{"pkg:generic/test@1.00"},
}

// create a test directory and a file in it
Expand Down

0 comments on commit 01f6dfa

Please sign in to comment.