Skip to content

Commit

Permalink
Split the sbom.Format interface by encode and decode use cases (#2186)
Browse files Browse the repository at this point in the history
* split up sbom.Format into encode and decode ops

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update cmd pkg to inject format configs

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* bump cyclonedx schema to 1.5

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* redact image metadata from github encoder tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add more testing around format decoder identify

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add test case for format version options

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix cli tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix CLI test

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* [wip] - review comments

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* keep encoder creation out of post load function

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* keep decider and identify functions

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add a few more doc comments

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* remove format encoder default function helpers

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* address PR feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* move back to streaming based decode functions

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* with common convention for encoder constructors

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix tests and allow for encoders to be created from cli options

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix cli tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* buffer reads from stdin to support seeking

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
  • Loading branch information
wagoodman committed Oct 25, 2023
1 parent 7315f83 commit 7392d60
Show file tree
Hide file tree
Showing 254 changed files with 10,861 additions and 3,125 deletions.
66 changes: 38 additions & 28 deletions cmd/syft/cli/commands/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/formats"
"github.com/anchore/syft/syft/formats/github"
"github.com/anchore/syft/syft/formats/syftjson"
"github.com/anchore/syft/syft/formats/table"
"github.com/anchore/syft/syft/formats/template"
"github.com/anchore/syft/syft/formats/text"
"github.com/anchore/syft/syft/format/cyclonedxjson"
"github.com/anchore/syft/syft/format/spdxjson"
"github.com/anchore/syft/syft/format/spdxtagvalue"
"github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
Expand All @@ -37,30 +35,33 @@ const (
)

type attestOptions struct {
options.Config `yaml:",inline" mapstructure:",squash"`
options.SingleOutput `yaml:",inline" mapstructure:",squash"`
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
options.Catalog `yaml:",inline" mapstructure:",squash"`
options.Attest `yaml:",inline" mapstructure:",squash"`
options.Config `yaml:",inline" mapstructure:",squash"`
options.Output `yaml:",inline" mapstructure:",squash"`
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
options.Catalog `yaml:",inline" mapstructure:",squash"`
options.Attest `yaml:",inline" mapstructure:",squash"`
}

func Attest(app clio.Application) *cobra.Command {
id := app.ID()

var allowableOutputs []string
for _, f := range formats.AllIDs() {
switch f {
case table.ID, text.ID, github.ID, template.ID:
continue
}
allowableOutputs = append(allowableOutputs, f.String())
}

opts := &attestOptions{
UpdateCheck: options.DefaultUpdateCheck(),
SingleOutput: options.SingleOutput{
AllowableOptions: allowableOutputs,
Output: syftjson.ID.String(),
Output: options.Output{
AllowMultipleOutputs: false,
AllowableOptions: []string{
string(syftjson.ID),
string(cyclonedxjson.ID),
string(spdxjson.ID),
string(spdxtagvalue.ID),
},
Outputs: []string{syftjson.ID.String()},
OutputFile: options.OutputFile{ // nolint:staticcheck
Enabled: false, // explicitly not allowed
},
OutputTemplate: options.OutputTemplate{
Enabled: false, // explicitly not allowed
},
},
Catalog: options.DefaultCatalog(),
}
Expand Down Expand Up @@ -95,15 +96,13 @@ func runAttest(id clio.Identification, opts *attestOptions, userInput string) er
return fmt.Errorf("unable to build SBOM: %w", err)
}

o := opts.Output

f, err := os.CreateTemp("", o)
f, err := os.CreateTemp("", "syft-attest-")
if err != nil {
return fmt.Errorf("unable to create temp file: %w", err)
}
defer os.Remove(f.Name())

writer, err := opts.SBOMWriter(f.Name())
writer, err := opts.SBOMWriter()
if err != nil {
return fmt.Errorf("unable to create SBOM writer: %w", err)
}
Expand All @@ -118,10 +117,21 @@ func runAttest(id clio.Identification, opts *attestOptions, userInput string) er
return fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
}

outputNames := opts.OutputNameSet()
var outputName string
switch outputNames.Size() {
case 0:
return fmt.Errorf("no output format specified")
case 1:
outputName = outputNames.List()[0]
default:
return fmt.Errorf("multiple output formats specified: %s", strings.Join(outputNames.List(), ", "))
}

// Select Cosign predicate type based on defined output type
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
var predicateType string
switch strings.ToLower(o) {
switch strings.ToLower(outputName) {
case "cyclonedx-json":
predicateType = "cyclonedx"
case "spdx-tag-value", "spdx-tv":
Expand Down
14 changes: 9 additions & 5 deletions cmd/syft/cli/commands/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/formats"
"github.com/anchore/syft/syft/format"
)

const (
Expand All @@ -23,7 +23,7 @@ const (

type ConvertOptions struct {
options.Config `yaml:",inline" mapstructure:",squash"`
options.MultiOutput `yaml:",inline" mapstructure:",squash"`
options.Output `yaml:",inline" mapstructure:",squash"`
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
}

Expand All @@ -33,6 +33,7 @@ func Convert(app clio.Application) *cobra.Command {

opts := &ConvertOptions{
UpdateCheck: options.DefaultUpdateCheck(),
Output: options.DefaultOutput(),
}

return app.SetupCommand(&cobra.Command{
Expand Down Expand Up @@ -63,10 +64,13 @@ func RunConvert(opts *ConvertOptions, userInput string) error {
return err
}

var reader io.ReadCloser
var reader io.ReadSeekCloser

if userInput == "-" {
reader = os.Stdin
// though os.Stdin is an os.File, it does not support seeking
// you will get errors such as "seek /dev/stdin: illegal seek".
// We need to buffer what we read.
reader = internal.NewBufferedSeeker(os.Stdin)
} else {
f, err := os.Open(userInput)
if err != nil {
Expand All @@ -78,7 +82,7 @@ func RunConvert(opts *ConvertOptions, userInput string) error {
reader = f
}

s, _, err := formats.Decode(reader)
s, _, _, err := format.Decode(reader)
if err != nil {
return fmt.Errorf("failed to decode SBOM: %w", err)
}
Expand Down
26 changes: 2 additions & 24 deletions cmd/syft/cli/commands/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/formats/template"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
Expand Down Expand Up @@ -55,14 +54,14 @@ const (

type packagesOptions struct {
options.Config `yaml:",inline" mapstructure:",squash"`
options.MultiOutput `yaml:",inline" mapstructure:",squash"`
options.Output `yaml:",inline" mapstructure:",squash"`
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
options.Catalog `yaml:",inline" mapstructure:",squash"`
}

func defaultPackagesOptions() *packagesOptions {
return &packagesOptions{
MultiOutput: options.DefaultOutput(),
Output: options.DefaultOutput(),
UpdateCheck: options.DefaultUpdateCheck(),
Catalog: options.DefaultCatalog(),
}
Expand Down Expand Up @@ -108,11 +107,6 @@ func validateArgs(cmd *cobra.Command, args []string, error string) error {

// nolint:funlen
func runPackages(id clio.Identification, opts *packagesOptions, userInput string) error {
err := validatePackageOutputOptions(&opts.MultiOutput)
if err != nil {
return err
}

writer, err := opts.SBOMWriter()
if err != nil {
return err
Expand Down Expand Up @@ -235,19 +229,3 @@ func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []art

return relationships
}

func validatePackageOutputOptions(cfg *options.MultiOutput) error {
var usesTemplateOutput bool
for _, o := range cfg.Outputs {
if o == template.ID.String() {
usesTemplateOutput = true
break
}
}

if usesTemplateOutput && cfg.OutputTemplatePath == "" {
return fmt.Errorf(`must specify path to template file when using "template" output format`)
}

return nil
}
7 changes: 5 additions & 2 deletions cmd/syft/cli/commands/poweruser.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/formats/syftjson"
"github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
Expand Down Expand Up @@ -42,6 +42,9 @@ func PowerUser(app clio.Application) *cobra.Command {
pkgs.FileClassification.Cataloger.Enabled = true
opts := &powerUserOptions{
Catalog: pkgs,
OutputFile: options.OutputFile{ // nolint:staticcheck
Enabled: true,
},
}

return app.SetupCommand(&cobra.Command{
Expand All @@ -62,7 +65,7 @@ func PowerUser(app clio.Application) *cobra.Command {

//nolint:funlen
func runPowerUser(id clio.Identification, opts *powerUserOptions, userInput string) error {
writer, err := opts.SBOMWriter(syftjson.Format())
writer, err := opts.SBOMWriter(syftjson.NewFormatEncoder())
if err != nil {
return err
}
Expand Down

0 comments on commit 7392d60

Please sign in to comment.