Skip to content

Commit

Permalink
Merge from cbctl 1.7.4
Browse files Browse the repository at this point in the history
  • Loading branch information
shoraniy authored and yonatan-shorani committed Nov 27, 2022
1 parent c56db54 commit 7245b2f
Show file tree
Hide file tree
Showing 24 changed files with 847 additions and 1,030 deletions.
25 changes: 24 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package cmd

import (
"fmt"
"github.com/anchore/stereoscope"
"github.com/anchore/syft/syft"
partybus "github.com/wagoodman/go-partybus"
"io/ioutil"
"os"

Expand Down Expand Up @@ -36,6 +39,7 @@ func init() {
initEventChan,
initLog,
initConfig,
initLibraryProxyBuses,
)
}

Expand All @@ -54,7 +58,7 @@ func addSubCommands() {
rootCmd.AddCommand(k8sobject.Cmd())
}

// initConfig will initialize the debug log, if set by user.
// initLog will initialize the debug log, if set by user.
func initLog() {
flag := rootCmd.Flag("debug")
if !flag.Changed {
Expand Down Expand Up @@ -92,6 +96,24 @@ func initEventChan() {
bufferSize := 10
eventChan := make(chan bus.Event, bufferSize)
bus.SetEventChan(eventChan)
logrus.Debug("Global event channel configured")
}

// initLibraryProxyBuses redirects third-party library events to the singleton event channel of this CLI
// currently redirects syft and stereoscope events
func initLibraryProxyBuses() {
eventBus := partybus.NewBus()
subscription := eventBus.Subscribe()
go func() {
for e := range subscription.Events() {
eventType := bus.EventType(e.Type)
bus.Publish(bus.NewEvent(eventType, e.Value, false))
}
}()

stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
logrus.Debug("Service bus proxies configured")
}

// initConfig will initialize the app config via viper.
Expand All @@ -103,6 +125,7 @@ func initConfig() {
}

config.LoadAppConfig()
logrus.Debug("Configuration loaded")
}

func setGlobalCliOptions() {
Expand Down
1 change: 1 addition & 0 deletions cmd/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func Cmd() *cobra.Command {
cmd.AddCommand(ScanCmd())
cmd.AddCommand(ValidateCmd())
cmd.AddCommand(PackagesCmd())
cmd.AddCommand(PayloadCmd())

cmd.PersistentFlags().StringVarP(
&opts.OutputFormat, "output", "o", "table", "output format of the result")
Expand Down
19 changes: 17 additions & 2 deletions cmd/image/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package image

import (
"fmt"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/vmware/carbon-black-cloud-container-cli/internal"
Expand Down Expand Up @@ -41,8 +40,24 @@ func PrintSBOM(input string) {
var msg string

registryHandler := scan.NewRegistryHandler()
scanner := scan.NewScanner()

img, err := registryHandler.LoadImage(input, opts.scanOption)
if err != nil {
msg := fmt.Sprintf("Failed to pull image for input %s", input)
e := cberr.NewError(cberr.ImageLoadErr, msg, err)
bus.Publish(bus.NewErrorEvent(e))
logrus.Errorln(e)
return
}
defer func() {
if err := img.Cleanup(); err != nil {
logrus.WithError(err).Errorf("failed to clean up files for image [%s]", input)
}
scan.Cleanup()
}()

generatedBom, err := registryHandler.GenerateSBOM(input, opts.scanOption)
generatedBom, err := scanner.GenerateSBOM(img, input, opts.scanOption)
if err != nil {
bus.Publish(bus.NewErrorEvent(err))
}
Expand Down
134 changes: 94 additions & 40 deletions cmd/image/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@ package image

import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"sync"
"strings"

progress "github.com/wagoodman/go-progress"

containersimage "github.com/containers/image/v5/image"
"github.com/containers/image/v5/transports/alltransports"
imagetype "github.com/containers/image/v5/types"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/vmware/carbon-black-cloud-container-cli/internal"
"github.com/vmware/carbon-black-cloud-container-cli/internal/bus"
"github.com/vmware/carbon-black-cloud-container-cli/internal/config"
"github.com/vmware/carbon-black-cloud-container-cli/internal/terminalui"
"github.com/vmware/carbon-black-cloud-container-cli/internal/util/printtool"
"github.com/vmware/carbon-black-cloud-container-cli/pkg/cberr"
"github.com/vmware/carbon-black-cloud-container-cli/pkg/model/image"
"github.com/vmware/carbon-black-cloud-container-cli/pkg/model/layers"
"github.com/vmware/carbon-black-cloud-container-cli/pkg/presenter"
"github.com/vmware/carbon-black-cloud-container-cli/pkg/scan"
)
Expand Down Expand Up @@ -72,50 +78,42 @@ func handleScan(input string) {
}

func actualScan(input string, handler *scan.Handler, buildStep, namespace string) (*image.ScannedImage, bool) {
var msg string

registryHandler := scan.NewRegistryHandler()

var generatedBom *scan.Bom
var imgLayers []layers.Layer
var errBom, errLayers error
var wg sync.WaitGroup
wg.Add(2)

go func() {
generatedBom, errBom = registryHandler.GenerateSBOM(input, opts.scanOption)
wg.Done()
}()

go func() {
imgLayers, errLayers = registryHandler.GenerateLayers(input, opts.scanOption)
wg.Done()
}()

wg.Wait()

if errBom != nil {
bus.Publish(bus.NewErrorEvent(errBom))
return nil, true
stage := &progress.Stage{Current: "Fetch image id"}
prog := &progress.Manual{Total: 1}
value := progress.StagedProgressable(&struct {
progress.Stager
progress.Progressable
}{
Stager: stage,
Progressable: prog,
})
bus.Publish(bus.NewEvent(bus.StartScanTryFetchImageID, value, false))
defer prog.SetCompleted()

operationID := uuid.New().String()
logrus.WithField("operation_id", operationID).Info("Starting an operation")

imageID, err := getImageID(input)
if imageID != "" && !opts.ForceScan && opts.presenterOption.OutputFormat != "cyclondx" {
if err == nil {
results, err := handler.GetImagesScanResultsFromBackendByImageID(imageID)
if err == nil {
return results, false
}
}
}

if generatedBom == nil {
msg = fmt.Sprintf("Generated sbom for %s is empty", input)
e := cberr.NewError(cberr.SBOMGenerationErr, msg, errBom)
bus.Publish(bus.NewErrorEvent(e))
logrus.Errorln(e)
stage.Current = "Done fetching image id"

scanner := scan.NewScanner()
generatedBom, imgLayers, hasErr := scanner.ExtractDataFromImage(input, opts.scanOption)
if hasErr {
return nil, true
}

if errLayers != nil {
// Not directly exposed to customers, so we don't treat this as fatal error yet
logrus.WithError(errLayers).Debugln(fmt.Sprintf("failed to calculate layers for image"))
}

handler.AttachData(generatedBom, imgLayers, buildStep, namespace)
handler.AttachData(generatedBom, imgLayers, buildStep, namespace, imageID)

result, err := handler.Scan(opts.scanOption)
result, err := handler.Scan(operationID, opts.scanOption)
if err != nil {
bus.Publish(bus.NewErrorEvent(err))
return nil, true
Expand All @@ -132,3 +130,59 @@ func actualScan(input string, handler *scan.Handler, buildStep, namespace string

return result, false
}

func getImageID(input string) (string, error) {
ctx := context.Background()
srcCtx := &imagetype.SystemContext{
// if a multi-arch image detected, pull the linux image by default
ArchitectureChoice: "amd64",
OSChoice: "linux",
DockerInsecureSkipTLSVerify: imagetype.OptionalBoolTrue,
}

src, err := parseImageSource(ctx, srcCtx, input)
if err != nil {
return "", err
}

defer func(src imagetype.ImageSource) {
_ = src.Close()
}(src)

img, err := containersimage.FromUnparsedImage(ctx, srcCtx, containersimage.UnparsedInstance(src, nil))
if err != nil {
return "", err
}

configBlob, err := img.ConfigBlob(ctx)
if err != nil {
return "", err
}

hash := sha256.Sum256(configBlob)

configDigest := hex.EncodeToString(hash[:])
if configDigest == "" {
return "", fmt.Errorf("empty image id")
}

configDigest = "sha256:" + configDigest

return configDigest, nil
}

// parseImageSource converts image URL-like string to an ImageSource.
// The caller must call .Close() on the returned ImageSource.
func parseImageSource(ctx context.Context, srcCtx *imagetype.SystemContext, name string) (imagetype.ImageSource, error) {
transport := alltransports.TransportFromImageName(name)
if transport == nil && !strings.Contains(name, ".tar") {
name = "docker://" + name
}

ref, err := alltransports.ParseImageName(name)
if err != nil {
return nil, err
}

return ref.NewImageSource(ctx, srcCtx)
}
Loading

0 comments on commit 7245b2f

Please sign in to comment.