Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into source-api-refactor-o…
Browse files Browse the repository at this point in the history
…n-location-refactor

Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman committed Jun 26, 2023
2 parents d8a9756 + 0d4f190 commit a02a0a0
Show file tree
Hide file tree
Showing 26 changed files with 577 additions and 585 deletions.
1 change: 1 addition & 0 deletions .chronicle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle
GLOW_CMD = $(TEMP_DIR)/glow

# Tool versions #################################
GOLANGCILINT_VERSION := v1.53.2
GOLANGCILINT_VERSION := v1.53.3
GOSIMPORTS_VERSION := v0.3.8
BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.18.2
YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v2.0.2
COSIGN_VERSION := v2.1.0
QUILL_VERSION := v0.2.0
GLOW_VERSION := v1.5.1

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ This default behavior can be overridden with the `default-image-pull-source` con

### Default Cataloger Configuration by scan type

Syft uses different default sets of catalogers depending on what it is scanning: a container image or a directory on disk. The default catalogers for an image scan assumes that package installation steps have already been completed. For example, Syft will identify Python packages that have egg or wheel metadata files under a site-packages directory, since this indicates software actually installed on an image.

However, if you are scanning a directory, Syft doesn't assume that all relevant software is installed, and will use catalogers that can identify declared dependencies that may not yet be installed on the final system: for example, dependencies listed in a Python requirements.txt.

You can override the list of enabled/disabled catalogers by using the "catalogers" keyword in the [Syft configuration file](https://github.com/anchore/syft#configuration).

##### Image Scanning:
- alpmdb
- apkdb
Expand Down
228 changes: 102 additions & 126 deletions cmd/syft/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/anchore/syft/cmd/syft/cli/packages"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/event"
Expand All @@ -34,17 +35,6 @@ func Run(_ context.Context, app *config.Application, args []string) error {
return err
}

writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return fmt.Errorf("unable to write to report destination: %w", err)
}

defer func() {
if err := writer.Close(); err != nil {
fmt.Printf("unable to close report destination: %+v", err)
}
}()

// note: must be a container image
userInput := args[0]

Expand All @@ -54,15 +44,15 @@ func Run(_ context.Context, app *config.Application, args []string) error {
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, userInput, writer),
execWorker(app, userInput),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func buildSBOM(app *config.Application, userInput string, writer sbom.Writer, errs chan error) ([]byte, error) {
func buildSBOM(app *config.Application, userInput string, errs chan error) (*sbom.SBOM, error) {
cfg := source.DetectConfig{
DefaultImageSource: app.DefaultImagePullSource,
}
Expand Down Expand Up @@ -115,135 +105,90 @@ func buildSBOM(app *config.Application, userInput string, writer sbom.Writer, er
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
}

// note: only works for single format no multi writer support
sBytes, err := writer.Bytes(*s)
if err != nil {
return nil, fmt.Errorf("unable to build SBOM bytes: %w", err)
}

return sBytes, nil
return s, nil
}

//nolint:funlen
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
sBytes, err := buildSBOM(app, userInput, writer, errs)
defer bus.Publish(partybus.Event{Type: event.Exit})

s, err := buildSBOM(app, userInput, errs)
if err != nil {
errs <- fmt.Errorf("unable to build SBOM: %w", err)
return
}

// TODO: add multi writer support
for _, o := range app.Outputs {
f, err := os.CreateTemp("", o)
if err != nil {
errs <- fmt.Errorf("unable to create temp file: %w", err)
return
}

defer f.Close()
defer os.Remove(f.Name())

if _, err := f.Write(sBytes); err != nil {
errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err)
return
}

// TODO: what other validation here besides binary name?
cmd := "cosign"
if !commandExists(cmd) {
errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
return
}

// 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) {
case "cyclonedx-json":
predicateType = "cyclonedx"
case "spdx-tag-value":
predicateType = "spdx"
case "spdx-json":
predicateType = "spdxjson"
default:
predicateType = "custom"
}

args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}

execCmd := exec.Command(cmd, args...)
execCmd.Env = os.Environ()
if app.Attest.Key != "" {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
} else {
// no key provided, use cosign's keyless mode
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
}

// bus adapter for ui to hook into stdout via an os pipe
r, w, err := os.Pipe()
if err != nil {
errs <- fmt.Errorf("unable to create os pipe: %w", err)
return
}
defer w.Close()

b := &busWriter{r: r, w: w, mon: progress.NewManual(-1)}
execCmd.Stdout = b
execCmd.Stderr = b
defer b.mon.SetCompleted()

// attest the SBOM
err = execCmd.Run()
if err != nil {
b.mon.SetError(err)
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
return
}
// note: ValidateOutputOptions ensures that there is no more than one output type
o := app.Outputs[0]

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

bus.Publish(partybus.Event{
Type: event.Exit,
Value: func() error { return nil },
})
}()
return errs
}
writer, err := options.MakeSBOMWriter(app.Outputs, f.Name(), app.OutputTemplatePath)
if err != nil {
errs <- fmt.Errorf("unable to create SBOM writer: %w", err)
return
}

func ValidateOutputOptions(app *config.Application) error {
err := packages.ValidateOutputOptions(app)
if err != nil {
return err
}
if err := writer.Write(*s); err != nil {
errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err)
return
}

if len(app.Outputs) > 1 {
return fmt.Errorf("multiple SBOM format is not supported for attest at this time")
}
// TODO: what other validation here besides binary name?
cmd := "cosign"
if !commandExists(cmd) {
errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
return
}

// cannot use table as default output format when using template output
if slices.Contains(app.Outputs, table.ID.String()) {
app.Outputs = []string{syftjson.ID.String()}
}
// 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) {
case "cyclonedx-json":
predicateType = "cyclonedx"
case "spdx-tag-value", "spdx-tv":
predicateType = "spdx"
case "spdx-json", "json":
predicateType = "spdxjson"
default:
predicateType = "custom"
}

return nil
}
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}

type busWriter struct {
w *os.File
r *os.File
hasWritten bool
mon *progress.Manual
}
execCmd := exec.Command(cmd, args...)
execCmd.Env = os.Environ()
if app.Attest.Key != "" {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
} else {
// no key provided, use cosign's keyless mode
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
}

log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation")

// bus adapter for ui to hook into stdout via an os pipe
r, w, err := os.Pipe()
if err != nil {
errs <- fmt.Errorf("unable to create os pipe: %w", err)
return
}
defer w.Close()

mon := progress.NewManual(-1)

func (b *busWriter) Write(p []byte) (n int, err error) {
if !b.hasWritten {
b.hasWritten = true
bus.Publish(
partybus.Event{
Type: event.AttestationStarted,
Expand All @@ -256,13 +201,44 @@ func (b *busWriter) Write(p []byte) (n int, err error) {
Context: "cosign",
},
Value: &monitor.ShellProgress{
Reader: b.r,
Manual: b.mon,
Reader: r,
Manual: mon,
},
},
)

execCmd.Stdout = w
execCmd.Stderr = w

// attest the SBOM
err = execCmd.Run()
if err != nil {
mon.SetError(err)
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
return
}

mon.SetCompleted()
}()
return errs
}

func ValidateOutputOptions(app *config.Application) error {
err := packages.ValidateOutputOptions(app)
if err != nil {
return err
}

if len(app.Outputs) > 1 {
return fmt.Errorf("multiple SBOM format is not supported for attest at this time")
}
return b.w.Write(p)

// cannot use table as default output format when using template output
if slices.Contains(app.Outputs, table.ID.String()) {
app.Outputs = []string{syftjson.ID.String()}
}

return nil
}

func commandExists(cmd string) bool {
Expand Down
8 changes: 1 addition & 7 deletions cmd/syft/cli/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ import (

func Run(_ context.Context, app *config.Application, args []string) error {
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return err
}

defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %w", err)
}
}()

// this can only be a SBOM file
userInput := args[0]

Expand Down
Loading

0 comments on commit a02a0a0

Please sign in to comment.