mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Add detailed location info to json artifact (#127)
* add detailed location info to json artifact Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * decompose json presenter Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
dc8dfc8457
commit
817ce61036
@ -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"
|
||||
],
|
||||
|
||||
31
syft/presenter/json/artifact.go
Normal file
31
syft/presenter/json/artifact.go
Normal file
@ -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
|
||||
}
|
||||
40
syft/presenter/json/document.go
Normal file
40
syft/presenter/json/document.go
Normal file
@ -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
|
||||
}
|
||||
42
syft/presenter/json/image.go
Normal file
42
syft/presenter/json/image.go
Normal file
@ -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
|
||||
}
|
||||
46
syft/presenter/json/location.go
Normal file
46
syft/presenter/json/location.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
@ -78,6 +86,7 @@ func TestJsonImgsPresenter(t *testing.T) {
|
||||
*img.SquashedTree().File("/somefile-1.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-2",
|
||||
@ -86,6 +95,7 @@ func TestJsonImgsPresenter(t *testing.T) {
|
||||
*img.SquashedTree().File("/somefile-2.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -4,12 +4,13 @@
|
||||
"name": "package-1",
|
||||
"version": "1.0.1",
|
||||
"type": "deb",
|
||||
"sources": [
|
||||
{
|
||||
"found-by": "",
|
||||
"found-by": [
|
||||
"the-cataloger-1"
|
||||
],
|
||||
"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": "",
|
||||
"found-by": [
|
||||
"the-cataloger-2"
|
||||
],
|
||||
"locations": [
|
||||
"/somefile-2.txt"
|
||||
]
|
||||
{
|
||||
"path": "/somefile-2.txt",
|
||||
"layer-index": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user