add package provider abstraction and update json document input

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-12-10 19:08:50 -05:00
parent 137be60f28
commit 0699e6a6ca
No known key found for this signature in database
GPG key ID: 5CB45AE22BAB7EA7
19 changed files with 1068 additions and 104 deletions

View file

@ -21,7 +21,6 @@ import (
"github.com/anchore/grype/internal/format"
"github.com/anchore/grype/internal/ui"
"github.com/anchore/grype/internal/version"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/source"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -177,8 +176,7 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
var provider vulnerability.Provider
var metadataProvider vulnerability.MetadataProvider
var packages []pkg.Package
var srcMetadata source.Metadata
var theDistro *distro.Distro
var context pkg.Context
var err error
var wg = &sync.WaitGroup{}
@ -186,6 +184,7 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
go func() {
defer wg.Done()
log.Debug("loading DB")
provider, metadataProvider, err = grype.LoadVulnerabilityDb(appConfig.Db.ToCuratorConfig(), appConfig.Db.AutoUpdate)
if err != nil {
errs <- fmt.Errorf("failed to load vulnerability db: %w", err)
@ -194,7 +193,8 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
go func() {
defer wg.Done()
srcMetadata, packages, theDistro, err = grype.Catalog(userInput, appConfig.ScopeOpt)
log.Debugf("gathering packages")
packages, context, err = pkg.Provide(userInput, appConfig.ScopeOpt)
if err != nil {
errs <- fmt.Errorf("failed to catalog: %w", err)
}
@ -205,7 +205,7 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
return
}
matches := grype.FindVulnerabilitiesForPackage(provider, theDistro, packages...)
matches := grype.FindVulnerabilitiesForPackage(provider, context.Distro, packages...)
// determine if there are any severities >= to the max allowable severity (which is optional).
// note: until the shared file lock in sqlittle is fixed the sqlite DB cannot be access concurrently,
@ -216,7 +216,7 @@ func startWorker(userInput string, failOnSeverity *vulnerability.Severity) <-cha
bus.Publish(partybus.Event{
Type: event.VulnerabilityScanningFinished,
Value: presenter.GetPresenter(appConfig.PresenterOpt, matches, packages, theDistro, srcMetadata, metadataProvider),
Value: presenter.GetPresenter(appConfig.PresenterOpt, matches, packages, context, metadataProvider),
})
}()
return errs

4
go.mod
View file

@ -7,8 +7,8 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200810141238-330bef18dbca
github.com/anchore/grype-db v0.0.0-20200929200644-6d1c82acc95e
github.com/anchore/stereoscope v0.0.0-20201203153145-3f9a05a624d7
github.com/anchore/syft v0.9.3-0.20201204184855-2d0c127419a3
github.com/anchore/stereoscope v0.0.0-20201210022249-091f9bddb42e
github.com/anchore/syft v0.10.0
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/dustin/go-humanize v1.0.0
github.com/facebookincubator/nvdtools v0.1.4

9
go.sum
View file

@ -114,6 +114,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/sqlittle v1.4.0 h1:vgYt0nAjhdf/hg52MjKJ84g/uTzBPfrvI+VUBrIghxA=
github.com/alicebob/sqlittle v1.4.0/go.mod h1:Co1L1qxHqCwf41puWhk2HOodojR0mcsAV4BIt8byZh8=
github.com/anchore/client-go v0.0.0-20201210022459-59e7a0749c74 h1:9kkKTIyXJC+/syUcY6KWxFoJZJ+GWwrIscF+gBY067k=
github.com/anchore/client-go v0.0.0-20201210022459-59e7a0749c74/go.mod h1:FaODhIA06mxO1E6R32JE0TL1JWZZkmjRIAd4ULvHUKk=
github.com/anchore/go-rpmdb v0.0.0-20201106153645-0043963c2e12 h1:xbeIbn5F52JVx3RUIajxCj8b0y+9lywspql4sFhcxWQ=
github.com/anchore/go-rpmdb v0.0.0-20201106153645-0043963c2e12/go.mod h1:juoyWXIj7sJ1IDl4E/KIfyLtovbs5XQVSIdaQifFQT8=
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8=
@ -126,14 +128,21 @@ github.com/anchore/grype-db v0.0.0-20200929200644-6d1c82acc95e h1:s0HmxxDuJyvgGB
github.com/anchore/grype-db v0.0.0-20200929200644-6d1c82acc95e/go.mod h1:LINmipRzG88vnJEWvgMMDVCFH1qZsj7+bjmpERlSyaA=
github.com/anchore/stereoscope v0.0.0-20201203153145-3f9a05a624d7 h1:G3LnRqHL/IIeQZTAMtDOJNYfSYsXLNCZX4DCiS0R0FY=
github.com/anchore/stereoscope v0.0.0-20201203153145-3f9a05a624d7/go.mod h1:2Jja/4l0zYggW52og+nn0rut4i+OYjCf9vTyrM8RT4E=
github.com/anchore/stereoscope v0.0.0-20201210022249-091f9bddb42e h1:vHUqHTvH9/oxdDDh1fxS9Ls9gWGytKO7XbbzcQ9MBwI=
github.com/anchore/stereoscope v0.0.0-20201210022249-091f9bddb42e/go.mod h1:/dHAFjYflH/1tzhdHAcnMCjprMch+YzHJKi59m/1KCM=
github.com/anchore/syft v0.9.2 h1:kRmquh8qOqH+/84S3/kOzj0cnGiqtW4f38Iz3TGrzXQ=
github.com/anchore/syft v0.9.2/go.mod h1:1vZpPrvAhEnpUsi4/+V3c9W0eGgSZLesStiKt/ujf6E=
github.com/anchore/syft v0.9.3-0.20201204184855-2d0c127419a3 h1:kJFcZZlhP5spei7uRon+2QzFTABjmzcJfeYh2Hje8KQ=
github.com/anchore/syft v0.9.3-0.20201204184855-2d0c127419a3/go.mod h1:1vZpPrvAhEnpUsi4/+V3c9W0eGgSZLesStiKt/ujf6E=
github.com/anchore/syft v0.10.0 h1:fN7wUauj560M6rjaRYBobpTDxciYQT9f1JQTJvyBRuQ=
github.com/anchore/syft v0.10.0/go.mod h1:U+cGFs4UkMRxkVgiJ1OtQHfemdDkk2Mpaq5Rw3rqHnI=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA=
github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs=
github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=

View file

@ -1,71 +1,26 @@
package grype
import (
"fmt"
"os"
"strings"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/grype/logger"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/matcher"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/grype/internal"
"github.com/anchore/grype/internal/bus"
"github.com/anchore/grype/internal/log"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/source"
"github.com/wagoodman/go-partybus"
)
func Catalog(userImageStr string, scopeOpt source.Scope) (source.Metadata, []pkg.Package, *distro.Distro, error) {
// handle explicit sbom input first
if strings.HasPrefix(userImageStr, "sbom:") {
// the user has explicitly hinted this is an sbom, if there is an issue return the error
filepath := strings.TrimPrefix(userImageStr, "sbom:")
sbomReader, err := os.Open(filepath)
if err != nil {
return source.Metadata{}, nil, nil, fmt.Errorf("unable to read sbom: %w", err)
}
sourceMetadata, catalog, theDistro, err := syft.CatalogFromJSON(sbomReader)
if err != nil {
return source.Metadata{}, nil, nil, err
}
return sourceMetadata, pkg.FromCatalog(catalog), theDistro, err
} else if internal.IsPipedInput() && userImageStr == "" {
// the user has not provided an image and stdin is a pipe, assume this to be an explicit sbom case
sourceMetadata, catalog, theDistro, err := syft.CatalogFromJSON(os.Stdin)
if err != nil {
return source.Metadata{}, nil, nil, err
}
return sourceMetadata, pkg.FromCatalog(catalog), theDistro, err
}
// the user has not hinted that this may be a sbom, but lets try that first... ignore failures and fallback to syft
if sbomReader, err := os.Open(userImageStr); err == nil {
sourceMetadata, catalog, theDistro, err := syft.CatalogFromJSON(sbomReader)
if err == nil {
return sourceMetadata, pkg.FromCatalog(catalog), theDistro, nil
}
}
// attempt to parse as an image (left syft handle this)
theSource, catalog, theDistro, err := syft.Catalog(userImageStr, scopeOpt)
func FindVulnerabilities(provider vulnerability.Provider, userImageStr string, scopeOpt source.Scope) (match.Matches, pkg.Context, []pkg.Package, error) {
packages, context, err := pkg.Provide(userImageStr, scopeOpt)
if err != nil {
return source.Metadata{}, nil, nil, err
}
return theSource.Metadata, pkg.FromCatalog(catalog), theDistro, nil
}
func FindVulnerabilities(provider vulnerability.Provider, userImageStr string, scopeOpt source.Scope) (match.Matches, source.Metadata, []pkg.Package, error) {
sourceMetadata, packages, theDistro, err := Catalog(userImageStr, scopeOpt)
if err != nil {
return match.Matches{}, source.Metadata{}, nil, err
return match.Matches{}, pkg.Context{}, nil, err
}
return FindVulnerabilitiesForPackage(provider, theDistro, packages...), sourceMetadata, packages, nil
return FindVulnerabilitiesForPackage(provider, context.Distro, packages...), context, packages, nil
}
func FindVulnerabilitiesForPackage(provider vulnerability.Provider, d *distro.Distro, packages ...pkg.Package) match.Matches {

11
grype/pkg/context.go Normal file
View file

@ -0,0 +1,11 @@
package pkg
import (
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/source"
)
type Context struct {
Source *source.Metadata
Distro *distro.Distro
}

56
grype/pkg/provider.go Normal file
View file

@ -0,0 +1,56 @@
package pkg
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"github.com/anchore/grype/internal"
"github.com/anchore/syft/syft/source"
)
var errDoesNotProvide = fmt.Errorf("cannot provide packages from the given source")
type providerConfig struct {
userInput string
scopeOpt *source.Scope
reader io.Reader
}
type provider func(cfg providerConfig) ([]Package, Context, error)
// Provide a set of packages and context metadata describing where they were sourced from.
func Provide(userInput string, scopeOpt source.Scope) ([]Package, Context, error) {
providers := []provider{
syftJSONProvider,
syftProvider, // important: we should try syft last
}
// aggregate stdin into a buffer that can be used across multiple providers dynamically
var previousStdin bytes.Buffer
for _, p := range providers {
cfg := providerConfig{
userInput: userInput,
scopeOpt: &scopeOpt,
}
if internal.IsPipedInput() && userInput == "" {
// this is a hint to all providers that there is already a reader available, don't try and derive one from user input
// however, the user input may be useful in situations where the reader isn't provided
// this reader is a combination of previous bytes read from stdin by other providers as well as what still is
// available from stdin. The Tee reader is to ensure that any read bytes from stdin are preserved.
cfg.reader = io.MultiReader(&previousStdin, io.TeeReader(os.Stdin, &previousStdin))
}
packages, ctx, err := p(cfg)
if !errors.Is(err, errDoesNotProvide) {
return packages, ctx, err
}
}
return nil, Context{}, errDoesNotProvide
}

View file

@ -0,0 +1,213 @@
package pkg
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/anchore/grype/grype/cpe"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/pkg"
syftJson "github.com/anchore/syft/syft/presenter/json"
"github.com/anchore/syft/syft/source"
)
func syftJSONProvider(config providerConfig) ([]Package, Context, error) {
var reader io.Reader
if config.reader != nil {
// the caller is explicitly hinting to use the given reader as input
reader = config.reader
} else {
// try and get a reader from the description
if strings.HasPrefix(config.userInput, "sbom:") {
// the user has explicitly hinted this is an sbom, if there is an issue return the error
filepath := strings.TrimPrefix(config.userInput, "sbom:")
sbomReader, err := os.Open(filepath)
if err != nil {
return nil, Context{}, fmt.Errorf("user hinted 'sbom:' but could read SBOM file: %w", err)
}
reader = sbomReader
}
// the user has not hinted that this may be a sbom, but lets try that first...
if sbomReader, err := os.Open(config.userInput); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, Context{}, errDoesNotProvide
}
} else {
reader = sbomReader
}
}
return parseSyftJSON(reader)
}
// partialSyftDoc is the final package shape for a select elements from a syft JSON document.
type partialSyftDoc struct {
Source syftJson.Source `json:"source"`
Artifacts []partialSyftPackage `json:"artifacts"`
Distro syftJson.Distribution `json:"distro"`
}
// partialSyftPackage is the final package shape for a select elements from a syft JSON package.
type partialSyftPackage struct {
packageBasicMetadata
packageCustomMetadata
}
// packageBasicMetadata contains non-ambiguous values (type-wise) from pkg.Package.
type packageBasicMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
Type pkg.Type `json:"type"`
Locations []source.Location `json:"locations"`
Licenses []string `json:"licenses"`
Language pkg.Language `json:"language"`
CPEs []string `json:"cpes"`
PURL string `json:"purl"`
}
// packageCustomMetadata contains ambiguous values (type-wise) from pkg.Package.
type packageCustomMetadata struct {
MetadataType pkg.MetadataType `json:"metadataType"`
Metadata interface{} `json:"metadata"`
}
// packageMetadataUnpacker is all values needed from Package to disambiguate ambiguous fields during json unmarshaling.
type packageMetadataUnpacker struct {
MetadataType pkg.MetadataType `json:"metadataType"`
Metadata json.RawMessage `json:"metadata"`
}
// JavaMetadata encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
type partialSyftJavaMetadata struct {
VirtualPath string `json:"virtualPath"`
Manifest *partialSyftJavaManifest `mapstructure:"Manifest" json:"manifest,omitempty"`
PomProperties *partialSyftPomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty"`
}
// PomProperties represents the fields of interest extracted from a Java archive's pom.xml file.
type partialSyftPomProperties struct {
GroupID string `mapstructure:"groupId" json:"groupId"`
ArtifactID string `mapstructure:"artifactId" json:"artifactId"`
}
// JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
type partialSyftJavaManifest struct {
Main map[string]string `json:"main,omitempty"`
}
// String returns the stringer representation for a syft package.
func (p partialSyftPackage) String() string {
return fmt.Sprintf("Pkg(type=%s, name=%s, version=%s)", p.Type, p.Name, p.Version)
}
// UnmarshalJSON is a custom unmarshaller for handling basic values and values with ambiguous types.
func (p *partialSyftPackage) UnmarshalJSON(b []byte) error {
var basic packageBasicMetadata
if err := json.Unmarshal(b, &basic); err != nil {
return err
}
p.packageBasicMetadata = basic
var unpacker packageMetadataUnpacker
if err := json.Unmarshal(b, &unpacker); err != nil {
return err
}
p.MetadataType = unpacker.MetadataType
switch p.MetadataType {
case pkg.RpmdbMetadataType:
var payload RpmdbMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
return err
}
p.Metadata = payload
case pkg.DpkgMetadataType:
var payload DpkgMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
return err
}
p.Metadata = payload
case pkg.JavaMetadataType:
var partialPayload partialSyftJavaMetadata
if err := json.Unmarshal(unpacker.Metadata, &partialPayload); err != nil {
return err
}
var artifact, group, name string
if partialPayload.PomProperties != nil {
artifact = partialPayload.PomProperties.ArtifactID
group = partialPayload.PomProperties.GroupID
}
if partialPayload.Manifest != nil {
if n, ok := partialPayload.Manifest.Main["Name"]; ok {
name = n
}
}
p.Metadata = JavaMetadata{
PomArtifactID: artifact,
PomGroupID: group,
ManifestName: name,
}
case "":
// there may be packages with no metadata, which is OK
default:
return fmt.Errorf("unsupported package metadata type: %+v", p.MetadataType)
}
return nil
}
// parseSyftJson attempts to loosely parse the available JSON for only the fields needed, not the exact syft JSON shape.
// This allows for some resiliency as the syft document shape changes over time (but not fool-proof).
func parseSyftJSON(reader io.Reader) ([]Package, Context, error) {
var doc partialSyftDoc
decoder := json.NewDecoder(reader)
if err := decoder.Decode(&doc); err != nil {
return nil, Context{}, errDoesNotProvide
}
var packages = make([]Package, len(doc.Artifacts))
for i, a := range doc.Artifacts {
cpes, err := cpe.NewSlice(a.CPEs...)
if err != nil {
return nil, Context{}, err
}
packages[i] = Package{
id: ID(i),
Name: a.Name,
Version: a.Version,
Locations: a.Locations,
Language: a.Language,
Licenses: a.Licenses,
Type: a.Type,
CPEs: cpes,
PURL: a.PURL,
Metadata: a.Metadata,
}
}
var theDistro *distro.Distro
if doc.Distro.Name != "" {
d, err := distro.NewDistro(distro.Type(doc.Distro.Name), doc.Distro.Version, doc.Distro.IDLike)
if err != nil {
return nil, Context{}, err
}
theDistro = &d
}
srcMetadata := doc.Source.ToSourceMetadata()
return packages, Context{
Source: &srcMetadata,
Distro: theDistro,
}, nil
}

View file

@ -0,0 +1,160 @@
package pkg
import (
"os"
"testing"
"github.com/anchore/syft/syft/distro"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/source"
"github.com/anchore/syft/syft/pkg"
)
func must(c pkg.CPE, e error) pkg.CPE {
if e != nil {
panic(e)
}
return c
}
func TestParseSyftJSON(t *testing.T) {
tests := []struct {
Fixture string
Packages []Package
Context Context
}{
{
Fixture: "test-fixtures/syft-alpine.json",
Packages: []Package{
{
id: 0,
Name: "alpine-baselayout",
Version: "3.2.0-r6",
Locations: []source.Location{
{
Path: "/lib/apk/db/installed",
FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c",
},
},
Language: "",
Licenses: []string{
"GPL-2.0-only",
},
Type: "rpm",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:a:*:alpine-baselayout:3.2.0-r6:*:*:*:*:*:*:*")),
must(pkg.NewCPE("cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r6:*:*:*:*:*:*:*")),
},
PURL: "pkg:alpine/alpine-baselayout@3.2.0-r6?arch=x86_64",
Metadata: RpmdbMetadata{SourceRpm: "a-source.srpm"},
},
{
id: 1,
Name: "fake",
Version: "1.2.0-r0",
Locations: []source.Location{
{
Path: "/lib/apk/db/installed",
FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c",
},
},
Language: "lang",
Licenses: []string{
"LGPL-3.0-or-later",
},
Type: "dpkg",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:a:*:gmp:6.2.0-r0:*:*:*:*:*:*:*")),
must(pkg.NewCPE("cpe:2.3:a:gmp:gmp:6.2.0-r0:*:*:*:*:*:*:*")),
},
PURL: "pkg:alpine/gmp@6.2.0-r0?arch=x86_64",
Metadata: DpkgMetadata{Source: "a-source"},
},
{
id: 1,
Name: "gmp",
Version: "6.2.0-r0",
Locations: []source.Location{
{
Path: "/lib/apk/db/installed",
FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c",
},
},
Language: "the-lang",
Licenses: []string{
"LGPL-3.0-or-later",
},
Type: "java-archive",
CPEs: []pkg.CPE{
must(pkg.NewCPE("cpe:2.3:a:*:gmp:6.2.0-r0:*:*:*:*:*:*:*")),
must(pkg.NewCPE("cpe:2.3:a:gmp:gmp:6.2.0-r0:*:*:*:*:*:*:*")),
},
PURL: "pkg:alpine/gmp@6.2.0-r0?arch=x86_64",
Metadata: JavaMetadata{
PomArtifactID: "aid",
PomGroupID: "gid",
ManifestName: "a-name",
},
},
},
Context: Context{
Source: &source.Metadata{
Scheme: source.ImageScheme,
ImageMetadata: source.ImageMetadata{
UserInput: "alpine:fake",
Scope: "Squashed",
Layers: []source.LayerMetadata{
{
MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
Digest: "sha256:50644c29ef5a27c9a40c393a73ece2479de78325cae7d762ef3cdc19bf42dd0a",
Size: 5570176,
},
},
Size: 15879684,
ID: "sha256:fadf1294c09213b20d4d6fc84109584e1c102d185c2cae15144a87d29de65c6d",
ManifestDigest: "sha256:1f6495428fb363e2d233e5df078b2b200635c4e51f0a3be34ecf09d44b547590",
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Tags: []string{
"alpine:fake",
},
//RawManifest: []byte("eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoyMTE2LCJkaWdlc3QiOiJzaGEyNTY6ZmFkZjEyOTRjMDkyMTNiMjBkNGQ2ZmM4NDEwOTU4NGUxYzEwMmQxODVjMmNhZTE1MTQ0YTg3ZDI5ZGU2NWM2ZCJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjo1ODQ0OTkyLCJkaWdlc3QiOiJzaGEyNTY6NTA2NDRjMjllZjVhMjdjOWE0MGMzOTNhNzNlY2UyNDc5ZGU3ODMyNWNhZTdkNzYyZWYzY2RjMTliZjQyZGQwYSJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjE2NzkzNiwiZGlnZXN0Ijoic2hhMjU2OmNjMGZmMWRkYWQ2ZmU0OTc4ZDgzMjYzMGE5MzAzODgzYWRjNTZlZGZjNzdjYWEzNjkyMjM5YzJkODFjZjVkMDAifSx7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoxMDE2Njc4NCwiZGlnZXN0Ijoic2hhMjU2OjNkZDJkYjQ4M2JjOWQ2YjU2MWNlNWNjMTEwNWUwYjZkMTk2MWNhMjQ5YTczNmJiYTgzNzFhYjI4ZWEzMDRmODQifSx7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMjUyOCwiZGlnZXN0Ijoic2hhMjU2OjkzY2Y0Y2ZiNjczYzdlMTZhOWU3NGY3MzFkNjc2N2I3MGI5MmEwYjdjOWY1OWQwNmVmZDcyZmJmZjUzNTM3MWMifV19"),
//RawConfig: []byte("eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiQXJnc0VzY2FwZWQiOnRydWUsIkltYWdlIjoic2hhMjU2OjJjOWQ1MzNiMmI2NGFiMTI4MmFlYTE2ZGYwZjlkYmYwYjNjZDQ3YWMxZTAyYjc1YTM3NjNiMmY0M2NjOWRlNWUiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY29udGFpbmVyIjoiYzJlMTM3OTEyYWU2MzdkNzBlMDJhMDVhYWEyM2U3N2JlY2I3Mzg5MDJmZDNjYWMyMjdkNDRlYjdlYzEwMmQ0OCIsImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCJzZWQgLWkgJ3MvVjowLjkuMTEtcjMvVjowLjkuOS1yMC8nIC9saWIvYXBrL2RiL2luc3RhbGxlZCJdLCJJbWFnZSI6InNoYTI1NjoyYzlkNTMzYjJiNjRhYjEyODJhZWExNmRmMGY5ZGJmMGIzY2Q0N2FjMWUwMmI3NWEzNzYzYjJmNDNjYzlkZTVlIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ2LjE2NzYxOTRaIiwiZG9ja2VyX3ZlcnNpb24iOiIxOS4wMy4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIwLTA1LTI5VDIxOjE5OjQ2LjE5MjA0NTk3MloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YzkyYzI0ODIzOWY4YzdiOWIzYzA2NzY1MDk1NDgxNWYzOTFiN2JjYjA5MDIzZjk4NDk3MmMwODJhY2UyYThkMCBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjAtMDUtMjlUMjE6MTk6NDYuMzYzNTE4MzQ1WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkIjoiMjAyMC0wOS0yNFQyMjoyNjo0NC4zMjk1NTc4WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jIHdnZXQgaHR0cDovL2RsLWNkbi5hbHBpbmVsaW51eC5vcmcvYWxwaW5lL3YzLjkvbWFpbi94ODZfNjQvbGlidm5jc2VydmVyLTAuOS4xMS1yMy5hcGsifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ1LjY3MDg1MzhaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgYXBrIGFkZCAgbGlidm5jc2VydmVyLTAuOS4xMS1yMy5hcGsifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ2LjE2NzYxOTRaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgc2VkIC1pICdzL1Y6MC45LjExLXIzL1Y6MC45LjktcjAvJyAvbGliL2Fway9kYi9pbnN0YWxsZWQifV0sIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo1MDY0NGMyOWVmNWEyN2M5YTQwYzM5M2E3M2VjZTI0NzlkZTc4MzI1Y2FlN2Q3NjJlZjNjZGMxOWJmNDJkZDBhIiwic2hhMjU2OmNjMGZmMWRkYWQ2ZmU0OTc4ZDgzMjYzMGE5MzAzODgzYWRjNTZlZGZjNzdjYWEzNjkyMjM5YzJkODFjZjVkMDAiLCJzaGEyNTY6M2RkMmRiNDgzYmM5ZDZiNTYxY2U1Y2MxMTA1ZTBiNmQxOTYxY2EyNDlhNzM2YmJhODM3MWFiMjhlYTMwNGY4NCIsInNoYTI1Njo5M2NmNGNmYjY3M2M3ZTE2YTllNzRmNzMxZDY3NjdiNzBiOTJhMGI3YzlmNTlkMDZlZmQ3MmZiZmY1MzUzNzFjIl19fQ=="),
},
Path: "",
},
Distro: func() *distro.Distro {
d, _ := distro.NewDistro(distro.Alpine, "3.12.0", "")
return &d
}(),
},
},
}
for _, test := range tests {
t.Run(test.Fixture, func(t *testing.T) {
fh, err := os.Open(test.Fixture)
if err != nil {
t.Fatalf("unable to open fixture: %+v", err)
}
pkgs, context, err := parseSyftJSON(fh)
if err != nil {
t.Fatalf("unable to parse: %+v", err)
}
context.Source.ImageMetadata.RawConfig = nil
context.Source.ImageMetadata.RawManifest = nil
for _, d := range deep.Equal(test.Packages, pkgs) {
t.Errorf("pkg diff: %s", d)
}
for _, d := range deep.Equal(test.Context, context) {
t.Errorf("ctx diff: %s", d)
}
})
}
}

View file

@ -0,0 +1,21 @@
package pkg
import (
"github.com/anchore/syft/syft"
)
func syftProvider(config providerConfig) ([]Package, Context, error) {
if config.scopeOpt == nil {
return nil, Context{}, errDoesNotProvide
}
theSource, catalog, theDistro, err := syft.Catalog(config.userInput, *config.scopeOpt)
if err != nil {
return nil, Context{}, err
}
return FromCatalog(catalog), Context{
Source: &theSource.Metadata,
Distro: theDistro,
}, nil
}

View file

@ -0,0 +1,521 @@
{
"artifacts": [
{
"name": "alpine-baselayout",
"version": "3.2.0-r6",
"type": "rpm",
"foundBy": "apkdb-cataloger",
"locations": [
{
"path": "/lib/apk/db/installed",
"layerID": "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c"
}
],
"licenses": [
"GPL-2.0-only"
],
"language": "",
"cpes": [
"cpe:2.3:a:*:alpine-baselayout:3.2.0-r6:*:*:*:*:*:*:*",
"cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r6:*:*:*:*:*:*:*"
],
"purl": "pkg:alpine/alpine-baselayout@3.2.0-r6?arch=x86_64",
"metadataType": "RpmdbMetadata",
"metadata": {
"sourceRpm": "a-source.srpm",
"package": "alpine-baselayout",
"originPackage": "alpine-baselayout",
"maintainer": "Natanael Copa <ncopa@alpinelinux.org>",
"version": "3.2.0-r6",
"license": "GPL-2.0-only",
"architecture": "x86_64",
"url": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout",
"description": "Alpine base dir structure and init scripts",
"size": 19917,
"installedSize": 409600,
"pullDependencies": "/bin/sh so:libc.musl-x86_64.so.1",
"pullChecksum": "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=",
"gitCommitOfApkPort": "e1c51734fa96fa4bac92e9f14a474324c67916fc",
"files": [
{
"path": "/dev"
},
{
"path": "/dev/pts"
},
{
"path": "/dev/shm"
},
{
"path": "/etc"
},
{
"path": "/etc/fstab",
"checksum": "Q11Q7hNe8QpDS531guqCdrXBzoA/o="
},
{
"path": "/etc/group",
"checksum": "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4="
},
{
"path": "/etc/hostname",
"checksum": "Q16nVwYVXP/tChvUPdukVD2ifXOmc="
},
{
"path": "/etc/hosts",
"checksum": "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU="
},
{
"path": "/etc/inittab",
"checksum": "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc="
},
{
"path": "/etc/modules",
"checksum": "Q1toogjUipHGcMgECgPJX64SwUT1M="
},
{
"path": "/etc/motd",
"checksum": "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA="
},
{
"path": "/etc/mtab",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "777",
"checksum": "Q1kiljhXXH1LlQroHsEJIkPZg2eiw="
},
{
"path": "/etc/passwd",
"checksum": "Q1TchuuLUfur0izvfZQZxgN/LJhB8="
},
{
"path": "/etc/profile",
"checksum": "Q1KpFb8kl5LvwXWlY3e58FNsjrI34="
},
{
"path": "/etc/protocols",
"checksum": "Q13FqXUnvuOpMDrH/6rehxuYAEE34="
},
{
"path": "/etc/services",
"checksum": "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY="
},
{
"path": "/etc/shadow",
"ownerUid": "0",
"ownerGid": "42",
"permissions": "640",
"checksum": "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA="
},
{
"path": "/etc/shells",
"checksum": "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA="
},
{
"path": "/etc/sysctl.conf",
"checksum": "Q14upz3tfnNxZkIEsUhWn7Xoiw96g="
},
{
"path": "/etc/apk"
},
{
"path": "/etc/conf.d"
},
{
"path": "/etc/crontabs"
},
{
"path": "/etc/crontabs/root",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "600",
"checksum": "Q1vfk1apUWI4yLJGhhNRd0kJixfvY="
},
{
"path": "/etc/init.d"
},
{
"path": "/etc/modprobe.d"
},
{
"path": "/etc/modprobe.d/aliases.conf",
"checksum": "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk="
},
{
"path": "/etc/modprobe.d/blacklist.conf",
"checksum": "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg="
},
{
"path": "/etc/modprobe.d/i386.conf",
"checksum": "Q1pnay/njn6ol9cCssL7KiZZ8etlc="
},
{
"path": "/etc/modprobe.d/kms.conf",
"checksum": "Q1ynbLn3GYDpvajba/ldp1niayeog="
},
{
"path": "/etc/modules-load.d"
},
{
"path": "/etc/network"
},
{
"path": "/etc/network/if-down.d"
},
{
"path": "/etc/network/if-post-down.d"
},
{
"path": "/etc/network/if-pre-up.d"
},
{
"path": "/etc/network/if-up.d"
},
{
"path": "/etc/opt"
},
{
"path": "/etc/periodic"
},
{
"path": "/etc/periodic/15min"
},
{
"path": "/etc/periodic/daily"
},
{
"path": "/etc/periodic/hourly"
},
{
"path": "/etc/periodic/monthly"
},
{
"path": "/etc/periodic/weekly"
},
{
"path": "/etc/profile.d"
},
{
"path": "/etc/profile.d/color_prompt",
"checksum": "Q10wL23GuSCVfumMRgakabUI6EsSk="
},
{
"path": "/etc/profile.d/locale",
"checksum": "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU="
},
{
"path": "/etc/sysctl.d"
},
{
"path": "/home"
},
{
"path": "/lib"
},
{
"path": "/lib/firmware"
},
{
"path": "/lib/mdev"
},
{
"path": "/lib/modules-load.d"
},
{
"path": "/lib/sysctl.d"
},
{
"path": "/lib/sysctl.d/00-alpine.conf",
"checksum": "Q1HpElzW1xEgmKfERtTy7oommnq6c="
},
{
"path": "/media"
},
{
"path": "/media/cdrom"
},
{
"path": "/media/floppy"
},
{
"path": "/media/usb"
},
{
"path": "/mnt"
},
{
"path": "/opt"
},
{
"path": "/proc"
},
{
"path": "/root",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "700"
},
{
"path": "/run"
},
{
"path": "/sbin"
},
{
"path": "/sbin/mkmntdirs",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "755",
"checksum": "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg="
},
{
"path": "/srv"
},
{
"path": "/sys"
},
{
"path": "/tmp",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "1777"
},
{
"path": "/usr"
},
{
"path": "/usr/lib"
},
{
"path": "/usr/lib/modules-load.d"
},
{
"path": "/usr/local"
},
{
"path": "/usr/local/bin"
},
{
"path": "/usr/local/lib"
},
{
"path": "/usr/local/share"
},
{
"path": "/usr/sbin"
},
{
"path": "/usr/share"
},
{
"path": "/usr/share/man"
},
{
"path": "/usr/share/misc"
},
{
"path": "/var"
},
{
"path": "/var/run",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "777",
"checksum": "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q="
},
{
"path": "/var/cache"
},
{
"path": "/var/cache/misc"
},
{
"path": "/var/empty",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "555"
},
{
"path": "/var/lib"
},
{
"path": "/var/lib/misc"
},
{
"path": "/var/local"
},
{
"path": "/var/lock"
},
{
"path": "/var/lock/subsys"
},
{
"path": "/var/log"
},
{
"path": "/var/mail"
},
{
"path": "/var/opt"
},
{
"path": "/var/spool"
},
{
"path": "/var/spool/mail",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "777",
"checksum": "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc="
},
{
"path": "/var/spool/cron"
},
{
"path": "/var/spool/cron/crontabs",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "777",
"checksum": "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA="
},
{
"path": "/var/tmp",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "1777"
}
]
}
},
{
"name": "fake",
"version": "1.2.0-r0",
"type": "dpkg",
"foundBy": "apkdb-cataloger",
"locations": [
{
"path": "/lib/apk/db/installed",
"layerID": "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c"
}
],
"licenses": [
"LGPL-3.0-or-later"
],
"language": "lang",
"cpes": [
"cpe:2.3:a:*:gmp:6.2.0-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:gmp:gmp:6.2.0-r0:*:*:*:*:*:*:*"
],
"purl": "pkg:alpine/gmp@6.2.0-r0?arch=x86_64",
"metadataType": "DpkgMetadata",
"metadata": {
"source": "a-source"
}
},
{
"name": "gmp",
"version": "6.2.0-r0",
"type": "java-archive",
"foundBy": "apkdb-cataloger",
"locations": [
{
"path": "/lib/apk/db/installed",
"layerID": "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c"
}
],
"licenses": [
"LGPL-3.0-or-later"
],
"language": "the-lang",
"cpes": [
"cpe:2.3:a:*:gmp:6.2.0-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:gmp:gmp:6.2.0-r0:*:*:*:*:*:*:*"
],
"purl": "pkg:alpine/gmp@6.2.0-r0?arch=x86_64",
"metadataType": "JavaMetadata",
"metadata": {
"pomProperties": {
"groupId": "gid",
"artifactId": "aid"
},
"manifest": {
"main": {
"Name": "a-name"
}
},
"package": "gmp",
"originPackage": "gmp",
"maintainer": "Natanael Copa <ncopa@alpinelinux.org>",
"version": "6.2.0-r0",
"license": "LGPL-3.0-or-later",
"architecture": "x86_64",
"url": "https://gmplib.org/",
"description": "A free library for arbitrary precision arithmetic",
"size": 220040,
"installedSize": 430080,
"pullDependencies": "so:libc.musl-x86_64.so.1",
"pullChecksum": "Q1IdUBW9Q7DiKxqItRI93JTPsylgg=",
"gitCommitOfApkPort": "238b6bccbab3b844079fa109d5cee096b81756d3",
"files": [
{
"path": "/usr"
},
{
"path": "/usr/lib"
},
{
"path": "/usr/lib/libgmp.so.10",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "777",
"checksum": "Q15mdJR0bVgCzxA6EspemNzt2PxJ0="
},
{
"path": "/usr/lib/libgmp.so.10.4.0",
"ownerUid": "0",
"ownerGid": "0",
"permissions": "755",
"checksum": "Q1hT4sjrKisl0oLyq41UhDM+5q2Z4="
}
]
}
}
],
"source": {
"type": "image",
"target": {
"userInput": "alpine:fake",
"imageID": "sha256:fadf1294c09213b20d4d6fc84109584e1c102d185c2cae15144a87d29de65c6d",
"manifestDigest": "sha256:1f6495428fb363e2d233e5df078b2b200635c4e51f0a3be34ecf09d44b547590",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"alpine:fake"
],
"imageSize": 15879684,
"scope": "Squashed",
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:50644c29ef5a27c9a40c393a73ece2479de78325cae7d762ef3cdc19bf42dd0a",
"size": 5570176
}
],
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoyMTE2LCJkaWdlc3QiOiJzaGEyNTY6ZmFkZjEyOTRjMDkyMTNiMjBkNGQ2ZmM4NDEwOTU4NGUxYzEwMmQxODVjMmNhZTE1MTQ0YTg3ZDI5ZGU2NWM2ZCJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjo1ODQ0OTkyLCJkaWdlc3QiOiJzaGEyNTY6NTA2NDRjMjllZjVhMjdjOWE0MGMzOTNhNzNlY2UyNDc5ZGU3ODMyNWNhZTdkNzYyZWYzY2RjMTliZjQyZGQwYSJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjE2NzkzNiwiZGlnZXN0Ijoic2hhMjU2OmNjMGZmMWRkYWQ2ZmU0OTc4ZDgzMjYzMGE5MzAzODgzYWRjNTZlZGZjNzdjYWEzNjkyMjM5YzJkODFjZjVkMDAifSx7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoxMDE2Njc4NCwiZGlnZXN0Ijoic2hhMjU2OjNkZDJkYjQ4M2JjOWQ2YjU2MWNlNWNjMTEwNWUwYjZkMTk2MWNhMjQ5YTczNmJiYTgzNzFhYjI4ZWEzMDRmODQifSx7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMjUyOCwiZGlnZXN0Ijoic2hhMjU2OjkzY2Y0Y2ZiNjczYzdlMTZhOWU3NGY3MzFkNjc2N2I3MGI5MmEwYjdjOWY1OWQwNmVmZDcyZmJmZjUzNTM3MWMifV19",
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiQXJnc0VzY2FwZWQiOnRydWUsIkltYWdlIjoic2hhMjU2OjJjOWQ1MzNiMmI2NGFiMTI4MmFlYTE2ZGYwZjlkYmYwYjNjZDQ3YWMxZTAyYjc1YTM3NjNiMmY0M2NjOWRlNWUiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY29udGFpbmVyIjoiYzJlMTM3OTEyYWU2MzdkNzBlMDJhMDVhYWEyM2U3N2JlY2I3Mzg5MDJmZDNjYWMyMjdkNDRlYjdlYzEwMmQ0OCIsImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCJzZWQgLWkgJ3MvVjowLjkuMTEtcjMvVjowLjkuOS1yMC8nIC9saWIvYXBrL2RiL2luc3RhbGxlZCJdLCJJbWFnZSI6InNoYTI1NjoyYzlkNTMzYjJiNjRhYjEyODJhZWExNmRmMGY5ZGJmMGIzY2Q0N2FjMWUwMmI3NWEzNzYzYjJmNDNjYzlkZTVlIiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ2LjE2NzYxOTRaIiwiZG9ja2VyX3ZlcnNpb24iOiIxOS4wMy4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIwLTA1LTI5VDIxOjE5OjQ2LjE5MjA0NTk3MloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YzkyYzI0ODIzOWY4YzdiOWIzYzA2NzY1MDk1NDgxNWYzOTFiN2JjYjA5MDIzZjk4NDk3MmMwODJhY2UyYThkMCBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjAtMDUtMjlUMjE6MTk6NDYuMzYzNTE4MzQ1WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX0seyJjcmVhdGVkIjoiMjAyMC0wOS0yNFQyMjoyNjo0NC4zMjk1NTc4WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jIHdnZXQgaHR0cDovL2RsLWNkbi5hbHBpbmVsaW51eC5vcmcvYWxwaW5lL3YzLjkvbWFpbi94ODZfNjQvbGlidm5jc2VydmVyLTAuOS4xMS1yMy5hcGsifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ1LjY3MDg1MzhaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgYXBrIGFkZCAgbGlidm5jc2VydmVyLTAuOS4xMS1yMy5hcGsifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTI0VDIyOjI2OjQ2LjE2NzYxOTRaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgc2VkIC1pICdzL1Y6MC45LjExLXIzL1Y6MC45LjktcjAvJyAvbGliL2Fway9kYi9pbnN0YWxsZWQifV0sIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1Njo1MDY0NGMyOWVmNWEyN2M5YTQwYzM5M2E3M2VjZTI0NzlkZTc4MzI1Y2FlN2Q3NjJlZjNjZGMxOWJmNDJkZDBhIiwic2hhMjU2OmNjMGZmMWRkYWQ2ZmU0OTc4ZDgzMjYzMGE5MzAzODgzYWRjNTZlZGZjNzdjYWEzNjkyMjM5YzJkODFjZjVkMDAiLCJzaGEyNTY6M2RkMmRiNDgzYmM5ZDZiNTYxY2U1Y2MxMTA1ZTBiNmQxOTYxY2EyNDlhNzM2YmJhODM3MWFiMjhlYTMwNGY4NCIsInNoYTI1Njo5M2NmNGNmYjY3M2M3ZTE2YTllNzRmNzMxZDY3NjdiNzBiOTJhMGI3YzlmNTlkMDZlZmQ3MmZiZmY1MzUzNzFjIl19fQ=="
}
},
"distro": {
"name": "alpine",
"version": "3.12.0",
"idLike": ""
},
"descriptor": {
"name": "syft",
"version": "[not provided]"
},
"schema": {
"version": "1.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-1.0.0.json"
}
}

View file

@ -28,15 +28,18 @@ type Document struct {
}
// NewDocument returns a CycloneDX Document object populated with the SBOM and vulnerability findings.
func NewDocument(packages []pkg.Package, matches match.Matches, srcMetadata source.Metadata, provider vulnerability.MetadataProvider) (Document, error) {
func NewDocument(packages []pkg.Package, matches match.Matches, srcMetadata *source.Metadata, provider vulnerability.MetadataProvider) (Document, error) {
versionInfo := version.FromBuild()
doc := Document{
XMLNs: "http://cyclonedx.org/schema/bom/1.2",
XMLNsV: "http://cyclonedx.org/schema/ext/vulnerability/1.0",
Version: 1,
SerialNumber: uuid.New().URN(),
BomDescriptor: syftCDX.NewBomDescriptor(internal.ApplicationName, versionInfo.Version, srcMetadata),
XMLNs: "http://cyclonedx.org/schema/bom/1.2",
XMLNsV: "http://cyclonedx.org/schema/ext/vulnerability/1.0",
Version: 1,
SerialNumber: uuid.New().URN(),
}
if srcMetadata != nil {
doc.BomDescriptor = syftCDX.NewBomDescriptor(internal.ApplicationName, versionInfo.Version, *srcMetadata)
}
// attach matches

View file

@ -14,12 +14,12 @@ import (
type Presenter struct {
results match.Matches
packages []pkg.Package
srcMetadata source.Metadata
srcMetadata *source.Metadata
metadataProvider vulnerability.MetadataProvider
}
// NewPresenter is a *Presenter constructor
func NewPresenter(results match.Matches, packages []pkg.Package, srcMetadata source.Metadata, metadataProvider vulnerability.MetadataProvider) *Presenter {
func NewPresenter(results match.Matches, packages []pkg.Package, srcMetadata *source.Metadata, metadataProvider vulnerability.MetadataProvider) *Presenter {
return &Presenter{
results: results,
packages: packages,

View file

@ -130,9 +130,9 @@ func TestCycloneDxPresenterImage(t *testing.T) {
// testing. At that time, this line will no longer be necessary.
//
// This value is sourced from the "version" node in "./test-fixtures/snapshot/TestCycloneDxImgsPresenter.golden"
s.Metadata.ImageMetadata.Digest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
s.Metadata.ImageMetadata.ManifestDigest = "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
pres := NewPresenter(matches, packages, s.Metadata, newMetadataMock())
pres := NewPresenter(matches, packages, &s.Metadata, newMetadataMock())
// run presenter
err = pres.Present(&buffer)
if err != nil {
@ -167,7 +167,7 @@ func TestCycloneDxPresenterDir(t *testing.T) {
t.Fatal(err)
}
pres := NewPresenter(matches, packages, s.Metadata, newMetadataMock())
pres := NewPresenter(matches, packages, &s.Metadata, newMetadataMock())
// run presenter
err = pres.Present(&buffer)

View file

@ -8,15 +8,13 @@ import (
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/grype/internal"
"github.com/anchore/grype/internal/version"
"github.com/anchore/syft/syft/distro"
syftJson "github.com/anchore/syft/syft/presenter/json"
"github.com/anchore/syft/syft/source"
)
// Document represents the JSON document to be presented
type Document struct {
Matches []Match `json:"matches"`
Source syftJson.Source `json:"source"`
Source *syftJson.Source `json:"source"`
Distro syftJson.Distribution `json:"distro"`
Descriptor Descriptor `json:"descriptor"`
}
@ -36,7 +34,7 @@ type MatchDetails struct {
}
// NewDocument creates and populates a new Document struct, representing the populated JSON document.
func NewDocument(packages []pkg.Package, d *distro.Distro, srcMetadata source.Metadata, matches match.Matches, metadataProvider vulnerability.MetadataProvider) (Document, error) {
func NewDocument(packages []pkg.Package, context pkg.Context, matches match.Matches, metadataProvider vulnerability.MetadataProvider) (Document, error) {
// we must preallocate the findings to ensure the JSON document does not show "null" when no matches are found
var findings = make([]Match, 0)
for m := range matches.Enumerate() {
@ -64,15 +62,19 @@ func NewDocument(packages []pkg.Package, d *distro.Distro, srcMetadata source.Me
)
}
src, err := syftJson.NewSource(srcMetadata)
if err != nil {
return Document{}, err
var src *syftJson.Source
if context.Source != nil {
theSrc, err := syftJson.NewSource(*context.Source)
if err != nil {
return Document{}, err
}
src = &theSrc
}
return Document{
Matches: findings,
Source: src,
Distro: syftJson.NewDistribution(d),
Distro: syftJson.NewDistribution(context.Distro),
Descriptor: Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,

View file

@ -7,33 +7,29 @@ import (
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/source"
)
// Presenter is a generic struct for holding fields needed for reporting
type Presenter struct {
matches match.Matches
packages []pkg.Package
distro *distro.Distro
srcMetadata source.Metadata
context pkg.Context
metadataProvider vulnerability.MetadataProvider
}
// NewPresenter is a *Presenter constructor
func NewPresenter(matches match.Matches, packages []pkg.Package, d *distro.Distro, srcMetadata source.Metadata, metadataProvider vulnerability.MetadataProvider) *Presenter {
func NewPresenter(matches match.Matches, packages []pkg.Package, context pkg.Context, metadataProvider vulnerability.MetadataProvider) *Presenter {
return &Presenter{
matches: matches,
packages: packages,
distro: d,
metadataProvider: metadataProvider,
srcMetadata: srcMetadata,
context: context,
}
}
// Present creates a JSON-based reporting
func (pres *Presenter) Present(output io.Writer) error {
doc, err := NewDocument(pres.packages, pres.distro, pres.srcMetadata, pres.matches, pres.metadataProvider)
doc, err := NewDocument(pres.packages, pres.context, pres.matches, pres.metadataProvider)
if err != nil {
return err
}

View file

@ -155,7 +155,11 @@ func TestJsonImgsPresenter(t *testing.T) {
t.Fatalf("failed to create scope: %+v", err)
}
pres := NewPresenter(matches, packages, &d, theSource.Metadata, newMetadataMock())
ctx := pkg.Context{
Source: &theSource.Metadata,
Distro: &d,
}
pres := NewPresenter(matches, packages, ctx, newMetadataMock())
// TODO: add a constructor for a match.Match when the data is better shaped
@ -271,7 +275,11 @@ func TestJsonDirsPresenter(t *testing.T) {
t.Fatalf("could not make distro: %+v", err)
}
pres := NewPresenter(matches, pkg.FromCatalog(catalog), &d, s.Metadata, newMetadataMock())
ctx := pkg.Context{
Source: &s.Metadata,
Distro: &d,
}
pres := NewPresenter(matches, pkg.FromCatalog(catalog), ctx, newMetadataMock())
// TODO: add a constructor for a match.Match when the data is better shaped
@ -321,7 +329,12 @@ func TestEmptyJsonPresenter(t *testing.T) {
t.Fatalf("could not make distro: %+v", err)
}
pres := NewPresenter(matches, []pkg.Package{}, &d, theSource.Metadata, nil)
ctx := pkg.Context{
Source: &theSource.Metadata,
Distro: &d,
}
pres := NewPresenter(matches, []pkg.Package{}, ctx, nil)
// run presenter
if err = pres.Present(&buffer); err != nil {

View file

@ -4,6 +4,13 @@
"type": "image",
"target": {
"userInput": "user-input",
"imageID": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"manifestDigest": "sha256:79f5982abf18af54158ed48386c3bc18dcba70a2b0de6f75ed583505d4de673a",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
],
"imageSize": 65,
"scope": "AllLayers",
"layers": [
{
@ -22,12 +29,8 @@
"size": 27
}
],
"size": 65,
"digest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
]
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxODAxLCJkaWdlc3QiOiJzaGEyNTY6MjczMTI1MWRjMzQ5NTFjMGU1MGZjYzY0M2I0YzVmNzQ5MjJkYWQxYTVkOThmMzAyYjUwNGNmNDZjZDVkOTM2OCJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMDQ4LCJkaWdlc3QiOiJzaGEyNTY6ZTE1OGI1N2Q2ZjVhOTZlZjVmZDIyZjJmZTc2YzcwYjViYTZmZjViMjYxOWY5ZDgzMTI1YjJhYWQwNDkyYWM3YiJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpkYTIxMDU2ZTdiZjQzMDhlY2VhMGMwODM2ODQ4YTdmZTkyZjM4ZmRjZjM1YmMwOWVlNmQ5OGU3YWI3YmVlZWJmIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MzU4NCwiZGlnZXN0Ijoic2hhMjU2OmYwZTE4YWE2MDMyYzI0NjU5YTljNzQxZmMzNmNhNTZmNTg5NzgyZWExMzIwNjFjY2Y2ZjUyYjk1MjQwM2RhOTQifV19",
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpudWxsLCJJbWFnZSI6InNoYTI1NjpiMjQ5NTQwZjc4NDFlMmQxYTVjOTA2NzBkODhhOTgzYmI2Njc3NDNlNTc1N2FmZWI3MzBkMTQ1ZjQyOGEwN2M0IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCIjKG5vcCkgQUREIGRpcjpjOTM3YzZhYTUwODkwN2UyODUwOWI2NDRhMTJmOGQ2YzY3ZDM0ZTk2OWY4M2IxNGRlZTkzZWExN2Q3NjkwMjhhIGluIC8gIl0sIkltYWdlIjoic2hhMjU2OmIyNDk1NDBmNzg0MWUyZDFhNWM5MDY3MGQ4OGE5ODNiYjY2Nzc0M2U1NzU3YWZlYjczMGQxNDVmNDI4YTA3YzQiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY3JlYXRlZCI6IjIwMjAtMDktMjNUMTE6NTI6NDMuMTI0NjY2OFoiLCJkb2NrZXJfdmVyc2lvbiI6IjE5LjAzLjEyIiwiaGlzdG9yeSI6W3siY3JlYXRlZCI6IjIwMjAtMDktMjNUMTE6NTI6NDIuODczMDEyNloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YWMzMmRhMjNkNTFlODAxZjAyZjkyNDEyM2VkMzA5OTBlYjNmMGZlYzFiOWVkNGYwYjA2YzI0ZTg4YjljMzY5NSBpbiAvc29tZWZpbGUtMS50eHQgIn0seyJjcmVhdGVkIjoiMjAyMC0wOS0yM1QxMTo1Mjo0Mi45ODY1OTg5WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSBBREQgZmlsZTpkZjNiNzQ0ZjU0YTliMTZiOWI5YWVkNDBlM2U5OGQ5Y2EyYjQ5ZjVhNzdkOWZhOGE5NzY5MGQ3YmFmNTg4ODIwIGluIC9zb21lZmlsZS0yLnR4dCAifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTIzVDExOjUyOjQzLjEyNDY2NjhaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBkaXI6YzkzN2M2YWE1MDg5MDdlMjg1MDliNjQ0YTEyZjhkNmM2N2QzNGU5NjlmODNiMTRkZWU5M2VhMTdkNzY5MDI4YSBpbiAvICJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OmUxNThiNTdkNmY1YTk2ZWY1ZmQyMmYyZmU3NmM3MGI1YmE2ZmY1YjI2MTlmOWQ4MzEyNWIyYWFkMDQ5MmFjN2IiLCJzaGEyNTY6ZGEyMTA1NmU3YmY0MzA4ZWNlYTBjMDgzNjg0OGE3ZmU5MmYzOGZkY2YzNWJjMDllZTZkOThlN2FiN2JlZWViZiIsInNoYTI1NjpmMGUxOGFhNjAzMmMyNDY1OWE5Yzc0MWZjMzZjYTU2ZjU4OTc4MmVhMTMyMDYxY2NmNmY1MmI5NTI0MDNkYTk0Il19fQ=="
}
},
"distro": {

View file

@ -110,6 +110,13 @@
"type": "image",
"target": {
"userInput": "user-input",
"imageID": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"manifestDigest": "sha256:79f5982abf18af54158ed48386c3bc18dcba70a2b0de6f75ed583505d4de673a",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
],
"imageSize": 65,
"scope": "AllLayers",
"layers": [
{
@ -128,12 +135,8 @@
"size": 27
}
],
"size": 65,
"digest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"
]
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjoxODAxLCJkaWdlc3QiOiJzaGEyNTY6MjczMTI1MWRjMzQ5NTFjMGU1MGZjYzY0M2I0YzVmNzQ5MjJkYWQxYTVkOThmMzAyYjUwNGNmNDZjZDVkOTM2OCJ9LCJsYXllcnMiOlt7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLCJzaXplIjoyMDQ4LCJkaWdlc3QiOiJzaGEyNTY6ZTE1OGI1N2Q2ZjVhOTZlZjVmZDIyZjJmZTc2YzcwYjViYTZmZjViMjYxOWY5ZDgzMTI1YjJhYWQwNDkyYWM3YiJ9LHsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpkYTIxMDU2ZTdiZjQzMDhlY2VhMGMwODM2ODQ4YTdmZTkyZjM4ZmRjZjM1YmMwOWVlNmQ5OGU3YWI3YmVlZWJmIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MzU4NCwiZGlnZXN0Ijoic2hhMjU2OmYwZTE4YWE2MDMyYzI0NjU5YTljNzQxZmMzNmNhNTZmNTg5NzgyZWExMzIwNjFjY2Y2ZjUyYjk1MjQwM2RhOTQifV19",
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpudWxsLCJJbWFnZSI6InNoYTI1NjpiMjQ5NTQwZjc4NDFlMmQxYTVjOTA2NzBkODhhOTgzYmI2Njc3NDNlNTc1N2FmZWI3MzBkMTQ1ZjQyOGEwN2M0IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6bnVsbH0sImNvbnRhaW5lcl9jb25maWciOnsiSG9zdG5hbWUiOiIiLCJEb21haW5uYW1lIjoiIiwiVXNlciI6IiIsIkF0dGFjaFN0ZGluIjpmYWxzZSwiQXR0YWNoU3Rkb3V0IjpmYWxzZSwiQXR0YWNoU3RkZXJyIjpmYWxzZSwiVHR5IjpmYWxzZSwiT3BlblN0ZGluIjpmYWxzZSwiU3RkaW5PbmNlIjpmYWxzZSwiRW52IjpbIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIl0sIkNtZCI6WyIvYmluL3NoIiwiLWMiLCIjKG5vcCkgQUREIGRpcjpjOTM3YzZhYTUwODkwN2UyODUwOWI2NDRhMTJmOGQ2YzY3ZDM0ZTk2OWY4M2IxNGRlZTkzZWExN2Q3NjkwMjhhIGluIC8gIl0sIkltYWdlIjoic2hhMjU2OmIyNDk1NDBmNzg0MWUyZDFhNWM5MDY3MGQ4OGE5ODNiYjY2Nzc0M2U1NzU3YWZlYjczMGQxNDVmNDI4YTA3YzQiLCJWb2x1bWVzIjpudWxsLCJXb3JraW5nRGlyIjoiIiwiRW50cnlwb2ludCI6bnVsbCwiT25CdWlsZCI6bnVsbCwiTGFiZWxzIjpudWxsfSwiY3JlYXRlZCI6IjIwMjAtMDktMjNUMTE6NTI6NDMuMTI0NjY2OFoiLCJkb2NrZXJfdmVyc2lvbiI6IjE5LjAzLjEyIiwiaGlzdG9yeSI6W3siY3JlYXRlZCI6IjIwMjAtMDktMjNUMTE6NTI6NDIuODczMDEyNloiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YWMzMmRhMjNkNTFlODAxZjAyZjkyNDEyM2VkMzA5OTBlYjNmMGZlYzFiOWVkNGYwYjA2YzI0ZTg4YjljMzY5NSBpbiAvc29tZWZpbGUtMS50eHQgIn0seyJjcmVhdGVkIjoiMjAyMC0wOS0yM1QxMTo1Mjo0Mi45ODY1OTg5WiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSBBREQgZmlsZTpkZjNiNzQ0ZjU0YTliMTZiOWI5YWVkNDBlM2U5OGQ5Y2EyYjQ5ZjVhNzdkOWZhOGE5NzY5MGQ3YmFmNTg4ODIwIGluIC9zb21lZmlsZS0yLnR4dCAifSx7ImNyZWF0ZWQiOiIyMDIwLTA5LTIzVDExOjUyOjQzLjEyNDY2NjhaIiwiY3JlYXRlZF9ieSI6Ii9iaW4vc2ggLWMgIyhub3ApIEFERCBkaXI6YzkzN2M2YWE1MDg5MDdlMjg1MDliNjQ0YTEyZjhkNmM2N2QzNGU5NjlmODNiMTRkZWU5M2VhMTdkNzY5MDI4YSBpbiAvICJ9XSwib3MiOiJsaW51eCIsInJvb3RmcyI6eyJ0eXBlIjoibGF5ZXJzIiwiZGlmZl9pZHMiOlsic2hhMjU2OmUxNThiNTdkNmY1YTk2ZWY1ZmQyMmYyZmU3NmM3MGI1YmE2ZmY1YjI2MTlmOWQ4MzEyNWIyYWFkMDQ5MmFjN2IiLCJzaGEyNTY6ZGEyMTA1NmU3YmY0MzA4ZWNlYTBjMDgzNjg0OGE3ZmU5MmYzOGZkY2YzNWJjMDllZTZkOThlN2FiN2JlZWViZiIsInNoYTI1NjpmMGUxOGFhNjAzMmMyNDY1OWE5Yzc0MWZjMzZjYTU2ZjU4OTc4MmVhMTMyMDYxY2NmNmY1MmI5NTI0MDNkYTk0Il19fQ=="
}
},
"distro": {

View file

@ -9,8 +9,6 @@ import (
"github.com/anchore/grype/grype/presenter/json"
"github.com/anchore/grype/grype/presenter/table"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/source"
)
// Presenter is the main interface other Presenters need to implement
@ -19,14 +17,14 @@ type Presenter interface {
}
// GetPresenter retrieves a Presenter that matches a CLI option
func GetPresenter(option Option, matches match.Matches, packages []pkg.Package, d *distro.Distro, srcMetadata source.Metadata, metadataProvider vulnerability.MetadataProvider) Presenter {
func GetPresenter(option Option, matches match.Matches, packages []pkg.Package, context pkg.Context, metadataProvider vulnerability.MetadataProvider) Presenter {
switch option {
case JSONPresenter:
return json.NewPresenter(matches, packages, d, srcMetadata, metadataProvider)
return json.NewPresenter(matches, packages, context, metadataProvider)
case TablePresenter:
return table.NewPresenter(matches, packages, metadataProvider)
case CycloneDxPresenter:
return cyclonedx.NewPresenter(matches, packages, srcMetadata, metadataProvider)
return cyclonedx.NewPresenter(matches, packages, context.Source, metadataProvider)
default:
return nil
}