diff --git a/json-schema/schema.json b/json-schema/schema.json index 10b1b447d87..df1dee8fc49 100644 --- a/json-schema/schema.json +++ b/json-schema/schema.json @@ -4,6 +4,37 @@ "artifacts": { "items": { "properties": { + "found-by": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "properties": { + "layer-index": { + "type": "integer" + }, + "path": { + "type": "string" + } + }, + "required": [ + "layer-index", + "path" + ], + "type": "object" + } + ] + }, + "type": "array" + }, "metadata": { "properties": { "architecture": { @@ -425,27 +456,6 @@ "name": { "type": "string" }, - "sources": { - "items": { - "properties": { - "found-by": { - "type": "string" - }, - "locations": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "found-by", - "locations" - ], - "type": "object" - }, - "type": "array" - }, "type": { "type": "string" }, @@ -454,8 +464,9 @@ } }, "required": [ + "found-by", + "locations", "name", - "sources", "type", "version" ], @@ -520,4 +531,4 @@ "artifacts" ], "type": "object" -} +} \ No newline at end of file diff --git a/syft/presenter/json/artifact.go b/syft/presenter/json/artifact.go new file mode 100644 index 00000000000..c4a218c77db --- /dev/null +++ b/syft/presenter/json/artifact.go @@ -0,0 +1,31 @@ +package json + +import ( + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/scope" +) + +type Artifact struct { + Name string `json:"name"` + Version string `json:"version"` + Type string `json:"type"` + FoundBy []string `json:"found-by"` + Locations Locations `json:"locations,omitempty"` + Metadata interface{} `json:"metadata,omitempty"` +} + +func NewArtifact(p *pkg.Package, s scope.Scope) (Artifact, error) { + locations, err := NewLocations(p, s) + if err != nil { + return Artifact{}, err + } + + return Artifact{ + Name: p.Name, + Version: p.Version, + Type: string(p.Type), + FoundBy: []string{p.FoundBy}, + Locations: locations, + Metadata: p.Metadata, + }, nil +} diff --git a/syft/presenter/json/document.go b/syft/presenter/json/document.go new file mode 100644 index 00000000000..b04c627b2a1 --- /dev/null +++ b/syft/presenter/json/document.go @@ -0,0 +1,40 @@ +package json + +import ( + "fmt" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/scope" +) + +type Document struct { + Artifacts []Artifact `json:"artifacts"` + Image *Image `json:"image,omitempty"` + Directory *string `json:"directory,omitempty"` +} + +func NewDocument(catalog *pkg.Catalog, s scope.Scope) (Document, error) { + doc := Document{ + Artifacts: make([]Artifact, 0), + } + + srcObj := s.Source() + switch src := srcObj.(type) { + case scope.ImageSource: + doc.Image = NewImage(src) + case scope.DirSource: + doc.Directory = &s.DirSrc.Path + default: + return Document{}, fmt.Errorf("unsupported source: %T", src) + } + + for _, p := range catalog.Sorted() { + art, err := NewArtifact(p, s) + if err != nil { + return Document{}, err + } + doc.Artifacts = append(doc.Artifacts, art) + } + + return doc, nil +} diff --git a/syft/presenter/json/image.go b/syft/presenter/json/image.go new file mode 100644 index 00000000000..a40aa118393 --- /dev/null +++ b/syft/presenter/json/image.go @@ -0,0 +1,42 @@ +package json + +import "github.com/anchore/syft/syft/scope" + +type Image struct { + Layers []Layer `json:"layers"` + Size int64 `json:"size"` + Digest string `json:"digest"` + MediaType string `json:"media-type"` + Tags []string `json:"tags"` +} + +type Layer struct { + MediaType string `json:"media-type"` + Digest string `json:"digest"` + Size int64 `json:"size"` +} + +func NewImage(src scope.ImageSource) *Image { + // populate artifacts... + tags := make([]string, len(src.Img.Metadata.Tags)) + for idx, tag := range src.Img.Metadata.Tags { + tags[idx] = tag.String() + } + img := Image{ + Digest: src.Img.Metadata.Digest, + Size: src.Img.Metadata.Size, + MediaType: string(src.Img.Metadata.MediaType), + Tags: tags, + Layers: make([]Layer, len(src.Img.Layers)), + } + + // populate image metadata + for idx, l := range src.Img.Layers { + img.Layers[idx] = Layer{ + MediaType: string(l.Metadata.MediaType), + Digest: l.Metadata.Digest, + Size: l.Metadata.Size, + } + } + return &img +} diff --git a/syft/presenter/json/location.go b/syft/presenter/json/location.go new file mode 100644 index 00000000000..19f1cbb3145 --- /dev/null +++ b/syft/presenter/json/location.go @@ -0,0 +1,46 @@ +package json + +import ( + "fmt" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/scope" +) + +type Locations interface{} + +type ImageLocation struct { + Path string `json:"path"` + LayerIndex uint `json:"layer-index"` +} + +func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) { + srcObj := s.Source() + switch src := srcObj.(type) { + case scope.ImageSource: + locations := make([]ImageLocation, len(p.Source)) + for idx := range p.Source { + entry, err := src.Img.FileCatalog.Get(p.Source[idx]) + if err != nil { + return nil, fmt.Errorf("unable to find layer index for source-idx=%d package=%s", idx, p.Name) + } + + artifactSource := ImageLocation{ + LayerIndex: entry.Source.Metadata.Index, + Path: string(p.Source[idx].Path), + } + + locations[idx] = artifactSource + } + return locations, nil + + case scope.DirSource: + locations := make([]string, len(p.Source)) + for idx := range p.Source { + locations[idx] = string(p.Source[idx].Path) + } + return locations, nil + default: + return nil, fmt.Errorf("unable to determine source: %T", src) + } +} diff --git a/syft/presenter/json/presenter.go b/syft/presenter/json/presenter.go index d703f95c24c..206178ed623 100644 --- a/syft/presenter/json/presenter.go +++ b/syft/presenter/json/presenter.go @@ -2,7 +2,6 @@ package json import ( "encoding/json" - "fmt" "io" "github.com/anchore/syft/syft/pkg" @@ -21,93 +20,10 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter { } } -type document struct { - Artifacts []artifact `json:"artifacts"` - Image *image `json:"image,omitempty"` - Directory *string `json:"directory,omitempty"` -} - -type image struct { - Layers []layer `json:"layers"` - Size int64 `json:"size"` - Digest string `json:"digest"` - MediaType string `json:"media-type"` - Tags []string `json:"tags"` -} - -type layer struct { - MediaType string `json:"media-type"` - Digest string `json:"digest"` - Size int64 `json:"size"` -} - -type source struct { - FoundBy string `json:"found-by"` - Locations []string `json:"locations"` -} - -type artifact struct { - Name string `json:"name"` - Version string `json:"version"` - Type string `json:"type"` - Sources []source `json:"sources"` - Metadata interface{} `json:"metadata,omitempty"` -} - func (pres *Presenter) Present(output io.Writer) error { - doc := document{ - Artifacts: make([]artifact, 0), - } - - srcObj := pres.scope.Source() - switch src := srcObj.(type) { - case scope.ImageSource: - // populate artifacts... - tags := make([]string, len(src.Img.Metadata.Tags)) - for idx, tag := range src.Img.Metadata.Tags { - tags[idx] = tag.String() - } - doc.Image = &image{ - Digest: src.Img.Metadata.Digest, - Size: src.Img.Metadata.Size, - MediaType: string(src.Img.Metadata.MediaType), - Tags: tags, - Layers: make([]layer, len(src.Img.Layers)), - } - - // populate image metadata - for idx, l := range src.Img.Layers { - doc.Image.Layers[idx] = layer{ - MediaType: string(l.Metadata.MediaType), - Digest: l.Metadata.Digest, - Size: l.Metadata.Size, - } - } - - case scope.DirSource: - doc.Directory = &pres.scope.DirSrc.Path - default: - return fmt.Errorf("unsupported source: %T", src) - } - - for _, p := range pres.catalog.Sorted() { - art := artifact{ - Name: p.Name, - Version: p.Version, - Type: string(p.Type), - Sources: make([]source, len(p.Source)), - Metadata: p.Metadata, - } - - for idx := range p.Source { - srcObj := source{ - FoundBy: p.FoundBy, - Locations: []string{string(p.Source[idx].Path)}, - } - art.Sources[idx] = srcObj - } - - doc.Artifacts = append(doc.Artifacts, art) + doc, err := NewDocument(pres.catalog, pres.scope) + if err != nil { + return err } enc := json.NewEncoder(output) diff --git a/syft/presenter/json/presenter_test.go b/syft/presenter/json/presenter_test.go index 23c805ef00c..537df1d29ec 100644 --- a/syft/presenter/json/presenter_test.go +++ b/syft/presenter/json/presenter_test.go @@ -24,11 +24,19 @@ func TestJsonDirsPresenter(t *testing.T) { Name: "package-1", Version: "1.0.1", Type: pkg.DebPkg, + FoundBy: "the-cataloger-1", + Source: []file.Reference{ + {Path: "/some/path/pkg1"}, + }, }) catalog.Add(pkg.Package{ Name: "package-2", Version: "2.0.1", Type: pkg.DebPkg, + FoundBy: "the-cataloger-2", + Source: []file.Reference{ + {Path: "/some/path/pkg1"}, + }, }) s, err := scope.NewScopeFromDir("/some/path", scope.AllLayersScope) @@ -77,7 +85,8 @@ func TestJsonImgsPresenter(t *testing.T) { Source: []file.Reference{ *img.SquashedTree().File("/somefile-1.txt"), }, - Type: pkg.DebPkg, + Type: pkg.DebPkg, + FoundBy: "the-cataloger-1", }) catalog.Add(pkg.Package{ Name: "package-2", @@ -85,7 +94,8 @@ func TestJsonImgsPresenter(t *testing.T) { Source: []file.Reference{ *img.SquashedTree().File("/somefile-2.txt"), }, - Type: pkg.DebPkg, + Type: pkg.DebPkg, + FoundBy: "the-cataloger-2", }) s, err := scope.NewScopeFromImage(img, scope.AllLayersScope) diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index d7ebd76f8f1..fafcf58eb3c 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -4,13 +4,23 @@ "name": "package-1", "version": "1.0.1", "type": "deb", - "sources": [] + "found-by": [ + "the-cataloger-1" + ], + "locations": [ + "/some/path/pkg1" + ] }, { "name": "package-2", "version": "2.0.1", "type": "deb", - "sources": [] + "found-by": [ + "the-cataloger-2" + ], + "locations": [ + "/some/path/pkg1" + ] } ], "directory": "/some/path" diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 279f2a8d3ac..15d20cb521c 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -4,12 +4,13 @@ "name": "package-1", "version": "1.0.1", "type": "deb", - "sources": [ + "found-by": [ + "the-cataloger-1" + ], + "locations": [ { - "found-by": "", - "locations": [ - "/somefile-1.txt" - ] + "path": "/somefile-1.txt", + "layer-index": 0 } ] }, @@ -17,12 +18,13 @@ "name": "package-2", "version": "2.0.1", "type": "deb", - "sources": [ + "found-by": [ + "the-cataloger-2" + ], + "locations": [ { - "found-by": "", - "locations": [ - "/somefile-2.txt" - ] + "path": "/somefile-2.txt", + "layer-index": 1 } ] }