mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
feat: add package supplier flag (#4131)
--------- Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
This commit is contained in:
parent
89470ecdd3
commit
6b48bd4b5e
@ -218,8 +218,9 @@ func getSource(ctx context.Context, opts *options.Catalog, userInput string, sou
|
||||
cfg := syft.DefaultGetSourceConfig().
|
||||
WithRegistryOptions(opts.Registry.ToOptions()).
|
||||
WithAlias(source.Alias{
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
Name: opts.Source.Name,
|
||||
Version: opts.Source.Version,
|
||||
Supplier: opts.Source.Supplier,
|
||||
}).
|
||||
WithExcludeConfig(source.ExcludeConfig{
|
||||
Paths: opts.Exclusions,
|
||||
|
||||
@ -259,6 +259,9 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
|
||||
|
||||
flags.StringVarP(&cfg.Source.BasePath, "base-path", "",
|
||||
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
|
||||
|
||||
flags.StringVarP(&cfg.Source.Supplier, "source-supplier", "",
|
||||
"the organization that supplied the component, which often may be the manufacturer, distributor, or repackager")
|
||||
}
|
||||
|
||||
func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) {
|
||||
|
||||
@ -43,9 +43,9 @@ over to the final syft 1.0 json output this option can be used to ease the trans
|
||||
|
||||
Note: long term support for this option is not guaranteed (it may change or break at any time)`)
|
||||
|
||||
descriptions.Add(&o.Template.Path, `path to the template file to use when rendering the output with the template output format.
|
||||
descriptions.Add(&o.Template.Path, `path to the template file to use when rendering the output with the template output format.
|
||||
Note that all template paths are based on the current syft-json schema`)
|
||||
descriptions.Add(&o.Template.Legacy, `if true, uses the go structs for the syft-json format for templating.
|
||||
descriptions.Add(&o.Template.Legacy, `if true, uses the go structs for the syft-json format for templating.
|
||||
if false, uses the syft-json output for templating (which follows the syft JSON schema exactly).
|
||||
|
||||
Note: long term support for this option is not guaranteed (it may change or break at any time)`)
|
||||
|
||||
@ -16,6 +16,8 @@ import (
|
||||
type sourceConfig struct {
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"`
|
||||
Supplier string `json:"supplier" yaml:"supplier" mapstructure:"supplier"`
|
||||
Source string `json:"source" yaml:"source" mapstructure:"source"`
|
||||
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
|
||||
File fileSource `json:"file" yaml:"file" mapstructure:"file"`
|
||||
Image imageSource `json:"image" yaml:"image" mapstructure:"image"`
|
||||
|
||||
@ -78,7 +78,10 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, image := range images {
|
||||
originalSBOM, _ := catalogFixtureImage(t, image, source.SquashedScope)
|
||||
|
||||
// we need a way to inject supplier into this test
|
||||
// supplier is not available as part of the SBOM Config API since the flag
|
||||
// is used in conjunction with the SourceConfig which is injected into generateSBOM during scan
|
||||
originalSBOM.Source.Supplier = "anchore"
|
||||
f := encoders.GetByString(test.name)
|
||||
require.NotNil(t, f)
|
||||
|
||||
|
||||
@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.36"
|
||||
JSONSchemaVersion = "16.0.37"
|
||||
)
|
||||
|
||||
3176
schema/json/schema-16.0.37.json
Normal file
3176
schema/json/schema-16.0.37.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.36/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.37/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@ -3047,6 +3047,9 @@
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"supplier": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@ -48,7 +48,7 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
packages := s.Artifacts.Packages.Sorted()
|
||||
components := make([]cyclonedx.Component, len(packages))
|
||||
for i, p := range packages {
|
||||
components[i] = helpers.EncodeComponent(p, locationSorter)
|
||||
components[i] = helpers.EncodeComponent(p, s.Source.Supplier, locationSorter)
|
||||
}
|
||||
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
||||
|
||||
@ -220,11 +220,22 @@ func toBomDescriptor(name, version string, srcMetadata source.Description) *cycl
|
||||
},
|
||||
},
|
||||
},
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
Properties: toBomProperties(srcMetadata),
|
||||
Component: toBomDescriptorComponent(srcMetadata),
|
||||
}
|
||||
}
|
||||
|
||||
func toBomSupplier(srcMetadata source.Description) *cyclonedx.OrganizationalEntity {
|
||||
if srcMetadata.Supplier != "" {
|
||||
return &cyclonedx.OrganizationalEntity{
|
||||
Name: srcMetadata.Supplier,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// used to indicate that a relationship listed under the syft artifact package can be represented as a cyclonedx dependency.
|
||||
// NOTE: CycloneDX provides the ability to describe components and their dependency on other components.
|
||||
// The dependency graph is capable of representing both direct and transitive relationships.
|
||||
@ -321,10 +332,11 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
log.Debugf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
|
||||
}
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
Type: cyclonedx.ComponentTypeContainer,
|
||||
Name: name,
|
||||
Version: version,
|
||||
BOMRef: string(bomRef),
|
||||
Type: cyclonedx.ComponentTypeContainer,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
case source.DirectoryMetadata:
|
||||
if name == "" {
|
||||
@ -337,9 +349,10 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
case source.FileMetadata:
|
||||
if name == "" {
|
||||
@ -352,9 +365,10 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: toBomSupplier(srcMetadata),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -389,7 +389,69 @@ func Test_toBomDescriptor(t *testing.T) {
|
||||
Name: "syft:image:labels:key1",
|
||||
Value: "value1",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with optional supplier is on the root component and bom metadata",
|
||||
args: args{
|
||||
name: "test-image",
|
||||
version: "1.0.0",
|
||||
srcMetadata: source.Description{
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
},
|
||||
want: &cyclonedx.Metadata{
|
||||
Timestamp: "",
|
||||
Lifecycles: nil,
|
||||
Tools: &cyclonedx.ToolsChoice{
|
||||
Components: &[]cyclonedx.Component{
|
||||
{
|
||||
Type: cyclonedx.ComponentTypeApplication,
|
||||
Author: "anchore",
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
Authors: nil,
|
||||
Component: &cyclonedx.Component{
|
||||
BOMRef: "",
|
||||
MIMEType: "",
|
||||
Type: "container",
|
||||
Supplier: &cyclonedx.OrganizationalEntity{
|
||||
Name: "optional-supplier",
|
||||
},
|
||||
Author: "",
|
||||
Publisher: "",
|
||||
Group: "",
|
||||
Name: "test-image",
|
||||
Version: "1.0.0",
|
||||
Description: "",
|
||||
Scope: "",
|
||||
Hashes: nil,
|
||||
Licenses: nil,
|
||||
Copyright: "",
|
||||
CPE: "",
|
||||
PackageURL: "",
|
||||
SWID: nil,
|
||||
Modified: nil,
|
||||
Pedigree: nil,
|
||||
ExternalReferences: nil,
|
||||
Properties: nil,
|
||||
Components: nil,
|
||||
Evidence: nil,
|
||||
ReleaseNotes: nil,
|
||||
},
|
||||
Manufacture: nil,
|
||||
Supplier: &cyclonedx.OrganizationalEntity{
|
||||
Name: "optional-supplier",
|
||||
},
|
||||
Licenses: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
||||
@ -257,13 +257,11 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
PackageChecksums: checksums,
|
||||
PackageExternalReferences: nil,
|
||||
PrimaryPackagePurpose: purpose,
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
},
|
||||
PackageCopyrightText: helpers.NOASSERTION,
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
PackageSupplier: toSPDXSupplier(s),
|
||||
PackageCopyrightText: helpers.NOASSERTION,
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
}
|
||||
|
||||
if purl != nil {
|
||||
@ -327,6 +325,23 @@ func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
return spdx.ElementID(helpers.SanitizeElementID(id))
|
||||
}
|
||||
|
||||
func toSPDXSupplier(s source.Description) *spdx.Supplier {
|
||||
supplier := helpers.NOASSERTION
|
||||
if s.Supplier != "" {
|
||||
supplier = s.Supplier
|
||||
}
|
||||
|
||||
supplierType := ""
|
||||
if supplier != helpers.NOASSERTION {
|
||||
supplierType = helpers.SUPPLIERORG
|
||||
}
|
||||
|
||||
return &spdx.Supplier{
|
||||
Supplier: supplier,
|
||||
SupplierType: supplierType,
|
||||
}
|
||||
}
|
||||
|
||||
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
//
|
||||
//nolint:funlen
|
||||
@ -391,7 +406,7 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
|
||||
// 7.6: Package Originator: may have single result for either Person or Organization,
|
||||
// or NOASSERTION
|
||||
// Cardinality: optional, one
|
||||
PackageSupplier: toPackageSupplier(p),
|
||||
PackageSupplier: toPackageSupplier(p, sbom.Source.Supplier),
|
||||
|
||||
PackageOriginator: toPackageOriginator(p),
|
||||
|
||||
@ -556,11 +571,18 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
|
||||
}
|
||||
}
|
||||
|
||||
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
|
||||
func toPackageSupplier(p pkg.Package, sbomSupplier string) *spdx.Supplier {
|
||||
kind, supplier := helpers.Supplier(p)
|
||||
if kind == "" || supplier == "" {
|
||||
supplier := helpers.NOASSERTION
|
||||
supplierType := ""
|
||||
if sbomSupplier != "" {
|
||||
supplier = sbomSupplier
|
||||
supplierType = helpers.SUPPLIERORG
|
||||
}
|
||||
return &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
Supplier: supplier,
|
||||
SupplierType: supplierType,
|
||||
}
|
||||
}
|
||||
return &spdx.Supplier{
|
||||
|
||||
@ -36,8 +36,9 @@ func Test_toFormatModel(t *testing.T) {
|
||||
name: "container",
|
||||
in: sbom.SBOM{
|
||||
Source: source.Description{
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Supplier: "Alpine Linux",
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:latest",
|
||||
ManifestDigest: "sha256:d34db33f",
|
||||
@ -61,7 +62,8 @@ func Test_toFormatModel(t *testing.T) {
|
||||
PackageName: "pkg-1",
|
||||
PackageVersion: "version-1",
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
Supplier: "Alpine Linux",
|
||||
SupplierType: "Organization",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -78,7 +80,8 @@ func Test_toFormatModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
Supplier: "Alpine Linux",
|
||||
SupplierType: "Organization",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -145,10 +145,21 @@ func containerSource(p *spdx.Package) source.Description {
|
||||
c := p.PackageChecksums[0]
|
||||
digest = fmt.Sprintf("%s:%s", fromChecksumAlgorithm(c.Algorithm), c.Value)
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.PackageSupplier != nil {
|
||||
// we also don't want NOASSERTION transferred to the syft format
|
||||
// NOASSERTION == ""
|
||||
if p.PackageSupplier.Supplier != helpers.NOASSERTION && p.PackageSupplier.SupplierType == helpers.SUPPLIERORG {
|
||||
supplier = p.PackageSupplier.Supplier
|
||||
}
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: id,
|
||||
Name: p.PackageName,
|
||||
Version: p.PackageVersion,
|
||||
ID: id,
|
||||
Name: p.PackageName,
|
||||
Version: p.PackageVersion,
|
||||
Supplier: supplier,
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: container,
|
||||
ID: id,
|
||||
@ -179,10 +190,16 @@ func fileSource(p *spdx.Package) source.Description {
|
||||
metadata, version = fileSourceMetadata(p)
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.PackageSupplier.Supplier != helpers.NOASSERTION {
|
||||
supplier = p.PackageSupplier.Supplier
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: string(p.PackageSPDXIdentifier),
|
||||
Name: p.PackageName,
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,17 +525,19 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
UserInput: "some-image:some-tag",
|
||||
ManifestDigest: "sha256:ab8b83234bc28f28d8e",
|
||||
},
|
||||
Name: "some-image",
|
||||
Version: "some-tag",
|
||||
Name: "some-image",
|
||||
Version: "some-tag",
|
||||
Supplier: "some-supplier",
|
||||
},
|
||||
packages: packages,
|
||||
relationships: relationships,
|
||||
},
|
||||
{
|
||||
name: ". directory source",
|
||||
name: ". directory source with supplier",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-.",
|
||||
Name: ".",
|
||||
ID: "DocumentRoot-Directory-.",
|
||||
Name: ".",
|
||||
Supplier: "some-supplier",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: ".",
|
||||
},
|
||||
@ -544,7 +546,7 @@ func Test_convertToAndFromFormat(t *testing.T) {
|
||||
relationships: relationships,
|
||||
},
|
||||
{
|
||||
name: "directory source",
|
||||
name: "directory source without supplier",
|
||||
source: source.Description{
|
||||
ID: "DocumentRoot-Directory-my-app",
|
||||
Name: "my-app",
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int) cyclonedx.Component {
|
||||
func EncodeComponent(p pkg.Package, supplier string, locationSorter func(a, b file.Location) int) cyclonedx.Component {
|
||||
props := EncodeProperties(p, "syft:package")
|
||||
|
||||
if p.Metadata != nil {
|
||||
@ -49,6 +49,7 @@ func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int)
|
||||
Name: p.Name,
|
||||
Group: encodeGroup(p),
|
||||
Version: p.Version,
|
||||
Supplier: encodeSupplier(p, supplier),
|
||||
PackageURL: p.PURL,
|
||||
Licenses: encodeLicenses(p),
|
||||
CPE: encodeSingleCPE(p),
|
||||
@ -61,6 +62,16 @@ func EncodeComponent(p pkg.Package, locationSorter func(a, b file.Location) int)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we eventually want to update this so that we can read "supplier" from different syft metadata
|
||||
func encodeSupplier(_ pkg.Package, sbomSupplier string) *cyclonedx.OrganizationalEntity {
|
||||
if sbomSupplier != "" {
|
||||
return &cyclonedx.OrganizationalEntity{
|
||||
Name: sbomSupplier,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeriveBomRef(p pkg.Package) string {
|
||||
// try and parse the PURL if possible and append syft id to it, to make
|
||||
// the purl unique in the BOM.
|
||||
|
||||
@ -152,7 +152,8 @@ func Test_encodeComponentProperties(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := EncodeComponent(test.input, file.LocationSorter(nil))
|
||||
sbomSupplier := ""
|
||||
c := EncodeComponent(test.input, sbomSupplier, file.LocationSorter(nil))
|
||||
if test.expected == nil {
|
||||
if c.Properties != nil {
|
||||
t.Fatalf("expected no properties, got: %+v", *c.Properties)
|
||||
@ -212,7 +213,8 @@ func Test_encodeCompomentType(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.pkg.ID()
|
||||
p := EncodeComponent(tt.pkg, file.LocationSorter(nil))
|
||||
sbomSupplier := ""
|
||||
p := EncodeComponent(tt.pkg, sbomSupplier, file.LocationSorter(nil))
|
||||
assert.Equal(t, tt.want, p)
|
||||
})
|
||||
}
|
||||
|
||||
@ -211,6 +211,16 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
}
|
||||
c := meta.Component
|
||||
|
||||
supplier := ""
|
||||
// First check component-level supplier
|
||||
if c.Supplier != nil && c.Supplier.Name != "" {
|
||||
supplier = c.Supplier.Name
|
||||
}
|
||||
// Fall back to metadata-level supplier if component supplier is not set
|
||||
if supplier == "" && meta.Supplier != nil && meta.Supplier.Name != "" {
|
||||
supplier = meta.Supplier.Name
|
||||
}
|
||||
|
||||
switch c.Type {
|
||||
case cyclonedx.ComponentTypeContainer:
|
||||
var labels map[string]string
|
||||
@ -220,7 +230,8 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: "",
|
||||
ID: "",
|
||||
Supplier: supplier,
|
||||
// TODO: can we decode alias name-version somehow? (it isn't be encoded in the first place yet)
|
||||
|
||||
Metadata: source.ImageMetadata{
|
||||
@ -236,6 +247,7 @@ func extractComponents(meta *cyclonedx.Metadata) source.Description {
|
||||
// TODO: this is lossy... we can't know if this is a file or a directory
|
||||
return source.Description{
|
||||
ID: "",
|
||||
Supplier: supplier,
|
||||
Metadata: source.FileMetadata{Path: c.Name},
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
|
||||
const NONE = "NONE"
|
||||
const NOASSERTION = "NOASSERTION"
|
||||
const SUPPLIERORG = "Organization"
|
||||
|
||||
func DownloadLocation(p pkg.Package) string {
|
||||
// 3.7: Package Download Location
|
||||
|
||||
@ -12,10 +12,16 @@ import (
|
||||
)
|
||||
|
||||
// Source object represents the thing that was cataloged
|
||||
// Note: syft currently makes no claims or runs any logic to determine the Supplier field below
|
||||
|
||||
// Instead, the Supplier can be determined by the user of syft and passed as a config or flag to help fulfill
|
||||
// the NTIA minimum elements. For mor information see the NTIA framing document below
|
||||
// https://www.ntia.gov/files/ntia/publications/framingsbom_20191112.pdf
|
||||
type Source struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Supplier string `json:"supplier,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Metadata interface{} `json:"metadata"`
|
||||
}
|
||||
@ -25,6 +31,7 @@ type sourceUnpacker struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Supplier string `json:"supplier,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Metadata json.RawMessage `json:"metadata"`
|
||||
Target json.RawMessage `json:"target"` // pre-v9 schema support
|
||||
@ -40,6 +47,7 @@ func (s *Source) UnmarshalJSON(b []byte) error {
|
||||
|
||||
s.Name = unpacker.Name
|
||||
s.Version = unpacker.Version
|
||||
s.Supplier = unpacker.Supplier
|
||||
s.Type = unpacker.Type
|
||||
s.ID = unpacker.ID
|
||||
|
||||
|
||||
@ -320,6 +320,7 @@ func toSourceModel(src source.Description) model.Source {
|
||||
ID: src.ID,
|
||||
Name: src.Name,
|
||||
Version: src.Version,
|
||||
Supplier: src.Supplier,
|
||||
Type: sourcemetadata.JSONName(src.Metadata),
|
||||
Metadata: src.Metadata,
|
||||
}
|
||||
|
||||
@ -56,19 +56,21 @@ func Test_toSourceModel(t *testing.T) {
|
||||
{
|
||||
name: "directory",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
},
|
||||
},
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "directory",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Type: "directory",
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: "some/path",
|
||||
Base: "some/base",
|
||||
@ -78,9 +80,10 @@ func Test_toSourceModel(t *testing.T) {
|
||||
{
|
||||
name: "file",
|
||||
src: source.Description{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
@ -88,10 +91,11 @@ func Test_toSourceModel(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: model.Source{
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Type: "file",
|
||||
ID: "test-id",
|
||||
Name: "some-name",
|
||||
Version: "some-version",
|
||||
Supplier: "optional-supplier",
|
||||
Type: "file",
|
||||
Metadata: source.FileMetadata{
|
||||
Path: "some/path",
|
||||
Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}},
|
||||
|
||||
@ -315,6 +315,7 @@ func toSyftSourceData(s model.Source) *source.Description {
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Version: s.Version,
|
||||
Supplier: s.Supplier,
|
||||
Metadata: s.Metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package source
|
||||
|
||||
type Alias struct {
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"`
|
||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||
Version string `json:"version" yaml:"version" mapstructure:"version"`
|
||||
Supplier string `json:"supplier" yaml:"supplier" mapstructure:"supplier"`
|
||||
}
|
||||
|
||||
func (a *Alias) IsEmpty() bool {
|
||||
|
||||
@ -5,5 +5,6 @@ type Description struct {
|
||||
ID string `hash:"ignore"` // the id generated from the parent source struct
|
||||
Name string `hash:"ignore"`
|
||||
Version string `hash:"ignore"`
|
||||
Supplier string `hash:"ignore"`
|
||||
Metadata interface{}
|
||||
}
|
||||
|
||||
@ -112,6 +112,7 @@ func (s directorySource) ID() artifact.ID {
|
||||
func (s directorySource) Describe() source.Description {
|
||||
name := cleanDirPath(s.config.Path, s.config.Base)
|
||||
version := ""
|
||||
supplier := ""
|
||||
if !s.config.Alias.IsEmpty() {
|
||||
a := s.config.Alias
|
||||
if a.Name != "" {
|
||||
@ -120,11 +121,15 @@ func (s directorySource) Describe() source.Description {
|
||||
if a.Version != "" {
|
||||
version = a.Version
|
||||
}
|
||||
if a.Supplier != "" {
|
||||
supplier = a.Supplier
|
||||
}
|
||||
}
|
||||
return source.Description{
|
||||
ID: string(s.id),
|
||||
Name: name,
|
||||
Version: version,
|
||||
ID: string(s.id),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: source.DirectoryMetadata{
|
||||
Path: s.config.Path,
|
||||
Base: s.config.Base,
|
||||
|
||||
@ -475,12 +475,13 @@ func Test_DirectorySource_ID(t *testing.T) {
|
||||
wantErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "to dir with name and version",
|
||||
name: "to dir with name and version and supplier",
|
||||
cfg: Config{
|
||||
Path: "./test-fixtures",
|
||||
Alias: source.Alias{
|
||||
Name: "name-me-that!",
|
||||
Version: "version-me-this!",
|
||||
Name: "name-me-that!",
|
||||
Version: "version-me-this!",
|
||||
Supplier: "some-supplier",
|
||||
},
|
||||
},
|
||||
want: artifact.ID("51a5f2a1536cf4b5220d4247814b07eec5862ab0547050f90e9ae216548ded7e"),
|
||||
@ -490,8 +491,9 @@ func Test_DirectorySource_ID(t *testing.T) {
|
||||
cfg: Config{
|
||||
Path: "./test-fixtures/image-simple",
|
||||
Alias: source.Alias{
|
||||
Name: "name-me-that!",
|
||||
Version: "version-me-this!",
|
||||
Name: "name-me-that!",
|
||||
Version: "version-me-this!",
|
||||
Supplier: "some-supplier",
|
||||
},
|
||||
},
|
||||
// note: this must match the previous value because the alias should trump the path info
|
||||
|
||||
@ -123,6 +123,7 @@ func (s fileSource) ID() artifact.ID {
|
||||
func (s fileSource) Describe() source.Description {
|
||||
name := path.Base(s.config.Path)
|
||||
version := s.digestForVersion
|
||||
supplier := ""
|
||||
if !s.config.Alias.IsEmpty() {
|
||||
a := s.config.Alias
|
||||
if a.Name != "" {
|
||||
@ -132,11 +133,16 @@ func (s fileSource) Describe() source.Description {
|
||||
if a.Version != "" {
|
||||
version = a.Version
|
||||
}
|
||||
|
||||
if a.Supplier != "" {
|
||||
supplier = a.Supplier
|
||||
}
|
||||
}
|
||||
return source.Description{
|
||||
ID: string(s.id),
|
||||
Name: name,
|
||||
Version: version,
|
||||
ID: string(s.id),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: source.FileMetadata{
|
||||
Path: s.config.Path,
|
||||
Digests: s.digests,
|
||||
|
||||
@ -51,6 +51,7 @@ func (s stereoscopeImageSource) Describe() source.Description {
|
||||
a := s.config.Alias
|
||||
|
||||
name := a.Name
|
||||
supplier := a.Supplier
|
||||
nameIfUnset := func(n string) {
|
||||
if name != "" {
|
||||
return
|
||||
@ -90,6 +91,7 @@ func (s stereoscopeImageSource) Describe() source.Description {
|
||||
ID: string(s.id),
|
||||
Name: name,
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: s.metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,21 @@ func TestPackagesCmdFlags(t *testing.T) {
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "source flags override bom metadata",
|
||||
args: []string{
|
||||
"scan",
|
||||
"--source-name", "custom-name",
|
||||
"--source-version", "custom-version",
|
||||
"--source-supplier", "custom-supplier",
|
||||
"-o", "json", coverageImage},
|
||||
assertions: []traitAssertion{
|
||||
assertInOutput("custom-name"),
|
||||
assertInOutput("custom-version"),
|
||||
assertInOutput("custom-supplier"),
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
// I haven't been able to reproduce locally yet, but in CI this has proven to be unstable:
|
||||
// For the same commit:
|
||||
// pass: https://github.com/anchore/syft/runs/4611344142?check_suite_focus=true
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user