mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
Introduce new format pattern + port json processing (#550)
* add new format pattern Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add syftjson format Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add internal formats helper Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add SBOM encode/decode to lib API Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove json presenter + update presenter tests to use common utils Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove presenter format enum type + add formats shim in presenter helper Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add MustCPE helper for tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update usage of format enum Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add test fixtures for encode/decode tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix integration test Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * migrate format detection to use reader Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * address review comments Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// Decoder is a function that can convert an SBOM document of a specific format from a reader into Syft native objects.
|
||||
type Decoder func(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distro.Distro, source.Scope, error)
|
||||
@@ -0,0 +1,12 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// Encoder is a function that can transform Syft native objects into an SBOM document of a specific format written to the given writer.
|
||||
type Encoder func(io.Writer, *pkg.Catalog, *source.Metadata, *distro.Distro, source.Scope) error
|
||||
@@ -0,0 +1,62 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEncodingNotSupported = errors.New("encoding not supported")
|
||||
ErrDecodingNotSupported = errors.New("decoding not supported")
|
||||
ErrValidationNotSupported = errors.New("validation not supported")
|
||||
)
|
||||
|
||||
type Format struct {
|
||||
Option Option
|
||||
encoder Encoder
|
||||
decoder Decoder
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewFormat(option Option, encoder Encoder, decoder Decoder, validator Validator) Format {
|
||||
return Format{
|
||||
Option: option,
|
||||
encoder: encoder,
|
||||
decoder: decoder,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
func (f Format) Encode(output io.Writer, catalog *pkg.Catalog, d *distro.Distro, metadata *source.Metadata, scope source.Scope) error {
|
||||
if f.encoder == nil {
|
||||
return ErrEncodingNotSupported
|
||||
}
|
||||
return f.encoder(output, catalog, metadata, d, scope)
|
||||
}
|
||||
|
||||
func (f Format) Decode(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distro.Distro, source.Scope, error) {
|
||||
if f.decoder == nil {
|
||||
return nil, nil, nil, source.UnknownScope, ErrDecodingNotSupported
|
||||
}
|
||||
return f.decoder(reader)
|
||||
}
|
||||
|
||||
func (f Format) Validate(reader io.Reader) error {
|
||||
if f.validator == nil {
|
||||
return ErrValidationNotSupported
|
||||
}
|
||||
|
||||
return f.validator(reader)
|
||||
}
|
||||
|
||||
func (f Format) Presenter(catalog *pkg.Catalog, metadata *source.Metadata, d *distro.Distro, scope source.Scope) *Presenter {
|
||||
if f.encoder == nil {
|
||||
return nil
|
||||
}
|
||||
return NewPresenter(f.encoder, catalog, metadata, d, scope)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package format
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
UnknownFormatOption Option = "UnknownFormatOption"
|
||||
JSONOption Option = "json"
|
||||
TextOption Option = "text"
|
||||
TableOption Option = "table"
|
||||
CycloneDxOption Option = "cyclonedx"
|
||||
SPDXTagValueOption Option = "spdx-tag-value"
|
||||
SPDXJSONOption Option = "spdx-json"
|
||||
)
|
||||
|
||||
var AllPresenters = []Option{
|
||||
JSONOption,
|
||||
TextOption,
|
||||
TableOption,
|
||||
CycloneDxOption,
|
||||
SPDXTagValueOption,
|
||||
SPDXJSONOption,
|
||||
}
|
||||
|
||||
type Option string
|
||||
|
||||
func ParseOption(userStr string) Option {
|
||||
switch strings.ToLower(userStr) {
|
||||
case string(JSONOption):
|
||||
return JSONOption
|
||||
case string(TextOption):
|
||||
return TextOption
|
||||
case string(TableOption):
|
||||
return TableOption
|
||||
case string(CycloneDxOption), "cyclone", "cyclone-dx":
|
||||
return CycloneDxOption
|
||||
case string(SPDXTagValueOption), "spdx", "spdx-tagvalue", "spdxtagvalue", "spdx-tv", "spdxtv":
|
||||
return SPDXTagValueOption
|
||||
case string(SPDXJSONOption), "spdxjson":
|
||||
return SPDXJSONOption
|
||||
default:
|
||||
return UnknownFormatOption
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/distro"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
srcMetadata *source.Metadata
|
||||
distro *distro.Distro
|
||||
scope source.Scope
|
||||
encoder Encoder
|
||||
}
|
||||
|
||||
func NewPresenter(encoder Encoder, catalog *pkg.Catalog, srcMetadata *source.Metadata, d *distro.Distro, scope source.Scope) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
srcMetadata: srcMetadata,
|
||||
distro: d,
|
||||
encoder: encoder,
|
||||
scope: scope,
|
||||
}
|
||||
}
|
||||
|
||||
func (pres *Presenter) Present(output io.Writer) error {
|
||||
return pres.encoder(output, pres.catalog, pres.srcMetadata, pres.distro, pres.scope)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package format
|
||||
|
||||
import "io"
|
||||
|
||||
// Validator reads the SBOM from the given reader and assesses whether the document conforms to the specific SBOM format.
|
||||
// The validator should positively confirm if the SBOM is not only the format but also has the minimal set of values
|
||||
// that the format requires. For example, all syftjson formatted documents have a schema section which should have
|
||||
// "anchore/syft" within the version --if this isn't found then the validator should raise an error. These active
|
||||
// assertions protect against "simple" format decoding validations that may lead to false positives (e.g. I decoded
|
||||
// json successfully therefore this must be the target format, however, all values are their default zero-value and
|
||||
// really represent a different format that also uses json)
|
||||
type Validator func(reader io.Reader) error
|
||||
Reference in New Issue
Block a user