diff --git a/README.md b/README.md index cd4784625..e0efab3ce 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,93 @@ # syft -A CLI tool and go library for generating a Software Bill of Materials from container images and filesystems. +A CLI tool and go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. + +[//]: # (TODO: add example gif here) + +**Features** +- Catalog container images and filesystems to discover packages and libraries. +- Supports packages and libraries from various ecosystems (APK, DEB, RPM, Ruby Bundles, Python Wheel/Egg/requirements.txt, JavaScript NPM/Yarn, Java JAR/EAR/WAR, Jenkins plugins JPI/HPI, Go modules) +- OS distribution detection (supports alpine, busybox, centos/redhat, debian/ubuntu flavored distributions) + +## Getting started + +To generate an SBOM for an image: +``` +syft +``` + +The above output includes only software that is visible in the container (i.e., the squashed representation of the image). +To include software from all image layers in the SBOM, regardless of its presence in the final image, provide `--scope all-layers`: + +``` +syft --scope all-layers +``` + +Syft can generate a SBOM from a variety of sources: +``` +# catalog a docker image tar (from the result of "docker image save ... -o image.tar" command) +syft docker-archive://path/to/image.tar + +# catalog a directory +syft dir://path/to/dir +``` + +By default Syft shows a summary table, however, more detailed `text` and `json` formats are also available. +``` +syft -o json +syft -o text +``` + +## Installation + +*NOTE: This tool hasn't been released yet, so these installation methods work right now* + +**Recommended** +```bash +# install the latest version to /usr/local/bin +curl -sSfL https://raw.githubusercontent.com/anchore/syft/master/install.sh | sh -s -- -b /usr/local/bin + +# install a specific version into a specific dir +curl -sSfL https://raw.githubusercontent.com/anchore/syft/master/install.sh | sh -s -b +``` + +**macOS** +```bash +brew tap anchore/syft +brew install syft +``` + +## Configuration + +Configuration search paths: + +- `.syft.yaml` +- `.syft/config.yaml` +- `~/.syft.yaml` +- `/syft/config.yaml` + +Configuration options (example values are the default): + +```yaml +# same as -o ; the output format of the SBOM report (options: table, text, json) +output: "table" + +# same as -s ; the search space to look for packages (options: all-layers, squashed) +scope: "squashed" + +# same as -q ; suppress all output (except for the SBOM report) +quiet: false + +log: + # use structured logging + structured: false + + # the log level; note: detailed logging suppress the ETUI + level: "error" + + # location to write the log file (default is not to have a log file) + file: "" + +# enable/disable checking for application updates on startup +check-for-app-update: true +``` diff --git a/cmd/cmd.go b/cmd/cmd.go index 49adba1c8..9171106f0 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -49,7 +49,7 @@ func setGlobalCliOptions() { // scan options flag := "scope" rootCmd.Flags().StringP( - "scope", "s", scope.AllLayersScope.String(), + "scope", "s", scope.SquashedScope.String(), fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options)) if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil { fmt.Printf("unable to bind flag '%s': %+v", flag, err) diff --git a/internal/logger/logrus.go b/internal/logger/logrus.go index bfe105a3a..104d5cef0 100644 --- a/internal/logger/logrus.go +++ b/internal/logger/logrus.go @@ -21,6 +21,7 @@ type LogrusConfig struct { type LogrusLogger struct { Config LogrusConfig Logger *logrus.Logger + Output io.Writer } type LogrusNestedLogger struct { @@ -71,6 +72,7 @@ func NewLogrusLogger(cfg LogrusConfig) *LogrusLogger { return &LogrusLogger{ Config: cfg, Logger: appLogger, + Output: output, } } diff --git a/internal/ui/etui/ephemeral_tui.go b/internal/ui/etui/ephemeral_tui.go index 18972d5b4..4fa6c4b3b 100644 --- a/internal/ui/etui/ephemeral_tui.go +++ b/internal/ui/etui/ephemeral_tui.go @@ -61,7 +61,12 @@ func OutputToEphemeralTUI(workerErrs <-chan error, subscription *partybus.Subscr fr.Close() frame.Close() // flush any errors to the screen before the report - fmt.Fprint(output, logBuffer.String()) + logWrapper, ok := log.Log.(*logger.LogrusLogger) + if ok { + fmt.Fprint(logWrapper.Output, logBuffer.String()) + } else { + fmt.Fprint(output, logBuffer.String()) + } } logWrapper, ok := log.Log.(*logger.LogrusLogger) if ok { @@ -107,7 +112,12 @@ eventLoop: isClosed = true // flush any errors to the screen before the report - fmt.Fprint(output, logBuffer.String()) + logWrapper, ok := log.Log.(*logger.LogrusLogger) + if ok { + fmt.Fprint(logWrapper.Output, logBuffer.String()) + } else { + fmt.Fprint(output, logBuffer.String()) + } if err := common.CatalogerFinishedHandler(e); err != nil { log.Errorf("unable to show %s event: %+v", e.Type, err) diff --git a/syft/cataloger/apkdb/parse_apk_db.go b/syft/cataloger/apkdb/parse_apk_db.go index 31e4317a1..39903b536 100644 --- a/syft/cataloger/apkdb/parse_apk_db.go +++ b/syft/cataloger/apkdb/parse_apk_db.go @@ -7,9 +7,8 @@ import ( "strconv" "strings" - "github.com/mitchellh/mapstructure" - "github.com/anchore/syft/syft/pkg" + "github.com/mitchellh/mapstructure" ) func parseApkDB(_ string, reader io.Reader) ([]pkg.Package, error) { diff --git a/syft/scope/option.go b/syft/scope/option.go index b67363039..fcfd49302 100644 --- a/syft/scope/option.go +++ b/syft/scope/option.go @@ -25,7 +25,7 @@ func ParseOption(userStr string) Option { switch strings.ToLower(userStr) { case strings.ToLower(SquashedScope.String()): return SquashedScope - case strings.ToLower(AllLayersScope.String()): + case "all-layers", strings.ToLower(AllLayersScope.String()): return AllLayersScope } return UnknownScope