Skip to content

Commit

Permalink
Support ResourceInfo as an embedded struct in ComponentInfo
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Rutkowski <[email protected]>
  • Loading branch information
mrutkows committed Apr 30, 2024
1 parent 19f8b1d commit f31c914
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 108 deletions.
76 changes: 6 additions & 70 deletions cmd/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,6 @@ const (

var VALID_SUBCOMMANDS_COMPONENT = []string{SUBCOMMAND_COMPONENT_LIST}

// type CDXComponent struct {
// Primary bool `json:"-"` // Proprietary: do NOT marshal/unmarshal
// Type string `json:"type,omitempty"` // Constraint: enum [see schema]
// Name string `json:"name,omitempty"`
// Version string `json:"version,omitempty"`
// Description string `json:"description,omitempty"`
// Group string `json:"group,omitempty"`
// BOMRef *CDXRefType `json:"bom-ref,omitempty"`
// MimeType string `json:"mime-type,omitempty"`
// Supplier *CDXOrganizationalEntity `json:"supplier,omitempty"`
// Author string `json:"author,omitempty"` // v1.6: deprecated.
// Publisher string `json:"publisher,omitempty"`
// Scope string `json:"scope,omitempty"` // Constraint: "enum": ["required","optional","excluded"]
// Hashes *[]CDXHash `json:"hashes,omitempty"`
// Licenses *[]CDXLicenseChoice `json:"licenses,omitempty"`
// Copyright string `json:"copyright,omitempty"`
// Cpe string `json:"cpe,omitempty"` // See: https://nvd.nist.gov/products/cpe
// Purl string `json:"purl,omitempty" scvs:"bom:resource:identifiers:purl"` // See: https://github.com/package-url/purl-spec
// Swid *CDXSwid `json:"swid,omitempty"` // See: https://www.iso.org/standard/65666.html
// Pedigree *CDXPedigree `json:"pedigree,omitempty"` // anon. type
// ExternalReferences *[]CDXExternalReference `json:"externalReferences,omitempty"`
// Components *[]CDXComponent `json:"components,omitempty"`
// Evidence *CDXComponentEvidence `json:"evidence,omitempty"` // v1.3: added
// Properties *[]CDXProperty `json:"properties,omitempty"` // v1.3: added
// Modified bool `json:"modified,omitempty" cdx:"deprecated"` // v1.4: deprecated
// ReleaseNotes *[]CDXReleaseNotes `json:"releaseNotes,omitempty"` // v1.4: added
// Signature *JSFSignature `json:"signature,omitempty"` // v1.4: added
// ModelCard *CDXModelCard `json:"modelCard,omitempty"` // v1.5: added
// Data *[]CDXComponentData `json:"data,omitempty"` // v1.5: added
// Authors *[]CDXOrganizationalContact `json:"authors,omitempty"` // v1.6: added
// Tags *[]string `json:"tags,omitempty" cdx:"+1.6"` // v1.6: added
// }
var COMPONENT_LIST_ROW_DATA = []ColumnFormatData{
*NewColumnFormatData(COMPONENT_FILTER_KEY_TYPE, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
*NewColumnFormatData(COMPONENT_FILTER_KEY_NAME, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
Expand All @@ -94,13 +62,6 @@ var VALID_COMPONENT_FILTER_KEYS = []string{
COMPONENT_FILTER_KEY_BOMREF,
}

// var COMPONENT_LIST_TITLES = []string{
// COMPONENT_FILTER_KEY_TYPE,
// COMPONENT_FILTER_KEY_NAME,
// COMPONENT_FILTER_KEY_VERSION,
// COMPONENT_FILTER_KEY_BOMREF,
// }

// Flags. Reuse query flag values where possible
const (
FLAG_COMPONENT_TYPE = "type"
Expand Down Expand Up @@ -155,25 +116,6 @@ func NewCommandComponent() *cobra.Command {
return command
}

func retrieveComponentType(cmd *cobra.Command) (componentTypes string, err error) {

componentTypes, err = cmd.Flags().GetString(FLAG_COMPONENT_TYPE)
if err != nil {
return
}

// TODO: parse "type" flag (comma-separated list of type names)
// TODO: validate each one is supported in the CDX Component Type enum.
// // validate component type(s) is a known keyword
// TODO: support multiple types (slice) and return "invalid" as an error
// if !schema.IsValidComponentType(componentTypes) {
// // invalid
// err = getLogger().Errorf("invalid type `%s`: `%s`", FLAG_COMPONENT_TYPE, componentType)
// }

return
}

func componentCmdImpl(cmd *cobra.Command, args []string) (err error) {
getLogger().Enter(args)
defer getLogger().Exit()
Expand All @@ -195,14 +137,8 @@ func componentCmdImpl(cmd *cobra.Command, args []string) (err error) {
// process filters supplied on the --where command flag
whereFilters, err := processWhereFlag(cmd)

// Process flag: --type
var types string
var commandFlags utils.ComponentCommandFlags
types, err = retrieveComponentType(cmd)

if err == nil {
commandFlags.Types = types
err = ListComponents(writer, utils.GlobalFlags.PersistentFlags, commandFlags, whereFilters)
err = ListComponents(writer, utils.GlobalFlags.PersistentFlags, whereFilters)
}

return
Expand All @@ -217,7 +153,7 @@ func processComponentListResults(err error) {
}

// NOTE: resourceType has already been validated
func ListComponents(writer io.Writer, persistentFlags utils.PersistentCommandFlags, componentFlags utils.ComponentCommandFlags, whereFilters []common.WhereFilter) (err error) {
func ListComponents(writer io.Writer, persistentFlags utils.PersistentCommandFlags, whereFilters []common.WhereFilter) (err error) {
getLogger().Enter()
defer getLogger().Exit()

Expand All @@ -238,7 +174,7 @@ func ListComponents(writer io.Writer, persistentFlags utils.PersistentCommandFla

// Hash all licenses within input file
getLogger().Infof("Scanning document for licenses...")
err = loadDocumentComponents(document, componentFlags.Types, whereFilters)
err = loadDocumentComponents(document, whereFilters)

if err != nil {
return
Expand All @@ -263,7 +199,7 @@ func ListComponents(writer io.Writer, persistentFlags utils.PersistentCommandFla
return
}

func loadDocumentComponents(document *schema.BOM, componentTypes string, whereFilters []common.WhereFilter) (err error) {
func loadDocumentComponents(document *schema.BOM, whereFilters []common.WhereFilter) (err error) {
getLogger().Enter()
defer getLogger().Exit(err)

Expand Down Expand Up @@ -295,8 +231,8 @@ func sortComponents(entries []multimap.Entry) {
sort.Slice(entries, func(i, j int) bool {
resource1 := (entries[i].Value).(schema.CDXResourceInfo)
resource2 := (entries[j].Value).(schema.CDXResourceInfo)
if resource1.Type != resource2.Type {
return resource1.Type < resource2.Type
if resource1.ResourceType != resource2.ResourceType {
return resource1.ResourceType < resource2.ResourceType
}
return resource1.Name < resource2.Name
})
Expand Down
3 changes: 0 additions & 3 deletions cmd/license_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ var LICENSE_LIST_SUPPORTED_FORMATS = MSG_SUPPORTED_OUTPUT_FORMATS_HELP +
strings.Join([]string{FORMAT_JSON, FORMAT_CSV, FORMAT_MARKDOWN}, ", ") +
" (default: json)"

// Title row names for formatted lists (reports)
//var LICENSE_LIST_TITLES_LICENSE_CHOICE = []string{"License.Id", "License.Name", "License.Url", "Expression", "License.Text.ContentType", "License.Text.Encoding", "License.Text.Content"}

// WARNING: Cobra will not recognize a subcommand if its `command.Use` is not a single
// word string that matches one of the `command.ValidArgs` set on the parent command
func NewCommandList() *cobra.Command {
Expand Down
7 changes: 5 additions & 2 deletions cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,13 @@ func prepareReportLineData(structIn interface{}, formatData []ColumnFormatData,
data, dataFound = mapStruct[columnData.DataKey]

if !dataFound {
err = getLogger().Errorf("data not found in structure: key: `%s`", columnData.DataKey)
return
// TODO: change back?
getLogger().Errorf("data not found in structure: key: `%s`", columnData.DataKey)
data = ""
//return
}

//fmt.Printf("data: `%v` (%T)\n", data, data)
switch typedData := data.(type) {
case string:
// replace line feeds with spaces in description
Expand Down
14 changes: 7 additions & 7 deletions cmd/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ var VALID_SUBCOMMANDS_RESOURCE = []string{SUBCOMMAND_RESOURCE_LIST}
// filter keys
// Note: these string values MUST match annotations for the ResourceInfo struct fields
const (
RESOURCE_FILTER_KEY_TYPE = "type"
RESOURCE_FILTER_KEY_NAME = "name"
RESOURCE_FILTER_KEY_VERSION = "version"
RESOURCE_FILTER_KEY_BOMREF = "bom-ref"
RESOURCE_FILTER_KEY_RESOURCE_TYPE = "resource-type"
RESOURCE_FILTER_KEY_NAME = "name"
RESOURCE_FILTER_KEY_VERSION = "version"
RESOURCE_FILTER_KEY_BOMREF = "bom-ref"
)

var RESOURCE_LIST_ROW_DATA = []ColumnFormatData{
*NewColumnFormatData(RESOURCE_FILTER_KEY_TYPE, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
*NewColumnFormatData(RESOURCE_FILTER_KEY_RESOURCE_TYPE, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
*NewColumnFormatData(RESOURCE_FILTER_KEY_NAME, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
*NewColumnFormatData(RESOURCE_FILTER_KEY_VERSION, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, false),
*NewColumnFormatData(RESOURCE_FILTER_KEY_BOMREF, DEFAULT_COLUMN_TRUNCATE_LENGTH, REPORT_SUMMARY_DATA_TRUE, REPORT_REPLACE_LINE_FEEDS_TRUE),
Expand Down Expand Up @@ -255,8 +255,8 @@ func sortResources(entries []multimap.Entry) {
sort.Slice(entries, func(i, j int) bool {
resource1 := (entries[i].Value).(schema.CDXResourceInfo)
resource2 := (entries[j].Value).(schema.CDXResourceInfo)
if resource1.Type != resource2.Type {
return resource1.Type < resource2.Type
if resource1.ResourceType != resource2.ResourceType {
return resource1.ResourceType < resource2.ResourceType
}
return resource1.Name < resource2.Name
})
Expand Down
6 changes: 3 additions & 3 deletions cmd/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ func DisplayStatsText(bom *schema.BOM, writer io.Writer) {
sort.Slice(entries, func(i, j int) bool {
resource1 := (entries[i].Value).(schema.CDXResourceInfo)
resource2 := (entries[j].Value).(schema.CDXResourceInfo)
if resource1.Type != resource2.Type {
return resource1.Type < resource2.Type
if resource1.ResourceType != resource2.ResourceType {
return resource1.ResourceType < resource2.ResourceType
}

return resource1.Name < resource2.Name
Expand All @@ -240,7 +240,7 @@ func DisplayStatsText(bom *schema.BOM, writer io.Writer) {

// Format line and write to output
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
resourceInfo.Type,
resourceInfo.ResourceType,
resourceInfo.Name,
resourceInfo.Version,
resourceInfo.BOMRef)
Expand Down
42 changes: 22 additions & 20 deletions schema/bom_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ func (bom *BOM) HashmapComponents(components []CDXComponent, whereFilters []comm
func (bom *BOM) HashmapComponent(cdxComponent CDXComponent, whereFilters []common.WhereFilter, root bool) (hashed bool, err error) {
getLogger().Enter()
defer getLogger().Exit(err)
var resourceInfo CDXResourceInfo
//var componentInfo CDXResourceInfo
var componentInfo CDXComponentInfo

if reflect.DeepEqual(cdxComponent, CDXComponent{}) {
getLogger().Warning("empty component object found")
Expand All @@ -98,40 +99,41 @@ func (bom *BOM) HashmapComponent(cdxComponent CDXComponent, whereFilters []commo
getLogger().Warningf("component named `%s` missing `version`", cdxComponent.Name)
}

if cdxComponent.BOMRef != nil && *cdxComponent.BOMRef == "" {
//if cdxComponent.BOMRef != nil && *cdxComponent.BOMRef == "" {
if cdxComponent.BOMRef == nil || *cdxComponent.BOMRef == "" {
getLogger().Warningf("component named `%s` missing `bom-ref`", cdxComponent.Name)
}

// hash any component w/o a license using special key name
resourceInfo.IsRoot = root
resourceInfo.Type = RESOURCE_TYPE_COMPONENT
resourceInfo.Component = cdxComponent
resourceInfo.Name = cdxComponent.Name
componentInfo.IsRoot = root
componentInfo.ResourceType = RESOURCE_TYPE_COMPONENT
componentInfo.Component = cdxComponent
componentInfo.Name = cdxComponent.Name
if cdxComponent.BOMRef != nil {
ref := *cdxComponent.BOMRef
resourceInfo.BOMRef = ref.String()
componentInfo.BOMRef = ref.String()
}
resourceInfo.Version = cdxComponent.Version
componentInfo.Version = cdxComponent.Version
if cdxComponent.Supplier != nil {
resourceInfo.SupplierProvider = cdxComponent.Supplier
componentInfo.SupplierProvider = cdxComponent.Supplier
}
resourceInfo.Properties = cdxComponent.Properties
componentInfo.Properties = cdxComponent.Properties

var match bool = true
if len(whereFilters) > 0 {
mapResourceInfo, _ := utils.MarshalStructToJsonMap(resourceInfo)
mapResourceInfo, _ := utils.MarshalStructToJsonMap(componentInfo)
match, _ = whereFilterMatch(mapResourceInfo, whereFilters)
}

if match {
hashed = true
bom.ComponentMap.Put(resourceInfo.BOMRef, resourceInfo)
bom.ResourceMap.Put(resourceInfo.BOMRef, resourceInfo)
bom.ComponentMap.Put(componentInfo.BOMRef, componentInfo)
bom.ResourceMap.Put(componentInfo.BOMRef, componentInfo.CDXResourceInfo)

getLogger().Tracef("Put: %s (`%s`), `%s`)",
resourceInfo.Name,
resourceInfo.Version,
resourceInfo.BOMRef)
getLogger().Infof("Put: %s (`%s`), `%s`)",
componentInfo.Name,
componentInfo.Version,
componentInfo.BOMRef)
}

// Recursively hash licenses for all child components (i.e., hierarchical composition)
Expand Down Expand Up @@ -196,12 +198,12 @@ func (bom *BOM) HashmapService(cdxService CDXService, whereFilters []common.Wher
getLogger().Warningf("service named `%s` missing `version`", cdxService.Name)
}

if cdxService.BOMRef == nil || *cdxService.BOMRef != "" {
if cdxService.BOMRef == nil || *cdxService.BOMRef == "" {
getLogger().Warningf("service named `%s` missing `bom-ref`", cdxService.Name)
}

// hash any component w/o a license using special key name
resourceInfo.Type = RESOURCE_TYPE_SERVICE
resourceInfo.ResourceType = RESOURCE_TYPE_SERVICE
resourceInfo.Service = cdxService
resourceInfo.Name = cdxService.Name
if cdxService.BOMRef != nil {
Expand All @@ -226,7 +228,7 @@ func (bom *BOM) HashmapService(cdxService CDXService, whereFilters []common.Wher
bom.ResourceMap.Put(resourceInfo.BOMRef, resourceInfo)

getLogger().Tracef("Put: [`%s`] %s (`%s`), `%s`)",
resourceInfo.Type,
resourceInfo.ResourceType,
resourceInfo.Name,
resourceInfo.Version,
resourceInfo.BOMRef,
Expand Down
2 changes: 1 addition & 1 deletion schema/cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ type CDXComponent struct {
Components *[]CDXComponent `json:"components,omitempty"`
Evidence *CDXComponentEvidence `json:"evidence,omitempty"` // v1.3: added
Properties *[]CDXProperty `json:"properties,omitempty"` // v1.3: added
Modified bool `json:"modified,omitempty" cdx:"deprecated"` // v1.4: deprecated
ReleaseNotes *[]CDXReleaseNotes `json:"releaseNotes,omitempty"` // v1.4: added
Signature *JSFSignature `json:"signature,omitempty"` // v1.4: added
ModelCard *CDXModelCard `json:"modelCard,omitempty"` // v1.5: added
Data *[]CDXComponentData `json:"data,omitempty"` // v1.5: added
Authors *[]CDXOrganizationalContact `json:"authors,omitempty"` // v1.6: added
Tags *[]string `json:"tags,omitempty" cdx:"+1.6"` // v1.6: added
Modified bool `json:"modified,omitempty" cdx:"deprecated"` // v1.4: deprecated
Author string `json:"author,omitempty"` // v1.6: deprecated
}

Expand Down
25 changes: 23 additions & 2 deletions schema/cyclonedx_abstractions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,37 @@ func IsValidResourceType(value string) bool {
// the CDX types CDXComponent and CDXService.
type CDXResourceInfo struct {
IsRoot bool
Type string `json:"type"`
BOMRef string `json:"bom-ref"`
ResourceType string `json:"resource-type"`
Group string `json:"group"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
BOMRef string `json:"bom-ref"`
SupplierProvider *CDXOrganizationalEntity
Properties *[]CDXProperty
Component CDXComponent
Service CDXService
}

// -------------------
// Components
// -------------------

// TODO: Supplier (*CDXOrganizationalEntity), Authors (*[]CDXOrganizationalContact)
// TODO: HasHashes, HasLicenses, HasPedigree, HasEvidence, HasComponents, HasReleaseNotes
// TODO: HasModelCard, HasData, HasTags, HasSignature (*JSFSignature)
// TODO: OmniborId (new), Swhid (new)
type CDXComponentInfo struct {
Type string `json:"type"`
Publisher string `json:"publisher,omitempty"`
Scope string `json:"scope,omitempty"`
Copyright string `json:"copyright,omitempty"`
Cpe string `json:"cpe,omitempty"` // See: https://nvd.nist.gov/products/cpe
Purl string `json:"purl,omitempty" scvs:"bom:resource:identifiers:purl"` // See: https://github.com/package-url/purl-spec
Swid *CDXSwid `json:"swid,omitempty"`
CDXResourceInfo
}

// -------------------
// Vulnerabilities
// -------------------
Expand Down

0 comments on commit f31c914

Please sign in to comment.