From f98ac7534b845e84bd5c1b0d424247514ef72a7f Mon Sep 17 00:00:00 2001 From: Benji Visser Date: Sat, 2 Sep 2023 21:40:33 -0700 Subject: [PATCH] Ingest directory scheme scans into xeol.io (#163) Signed-off-by: Benji Visser --- cmd/root.go | 16 +++++++- internal/xeolio/request.go | 5 +-- internal/xeolio/source.go | 76 ++++++++++++++++++++++++++++++++++++++ xeol/report/model.go | 3 +- 4 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 internal/xeolio/source.go diff --git a/cmd/root.go b/cmd/root.go index 7666c233..9a317205 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -260,6 +260,7 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) var wg = &sync.WaitGroup{} var loadedDB, gatheredPackages bool var policies []policy.Policy + var eventSourceScheme source.Scheme x := xeolio.NewXeolClient(appConfig.APIKey) wg.Add(3) @@ -294,6 +295,7 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) errs <- fmt.Errorf("failed to catalog: %w", err) return } + eventSourceScheme = xeolio.EventSourceScheme(sbom.Source) gatheredPackages = true }() wg.Wait() @@ -332,11 +334,16 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) for _, p := range policies { switch p.GetPolicyType() { case types.PolicyTypeNotary: + // Notary policy is only applicable to images + if eventSourceScheme != source.ImageScheme { + continue + } shouldFailScan, res := p.Evaluate(allMatches, appConfig.ProjectName, userInput) imageVerified = res.GetVerified() if shouldFailScan { failScan = true } + case types.PolicyTypeEol: shouldFailScan, _ := p.Evaluate(allMatches, appConfig.ProjectName, userInput) if shouldFailScan { @@ -354,13 +361,18 @@ func startWorker(userInput string, failOnEolFound bool, eolMatchDate time.Time) return } + eventSource, err := xeolio.NewEventSource(sbom.Source) + if err != nil { + errs <- fmt.Errorf("failed to create event source: %w", err) + return + } + if err := x.SendEvent(report.XeolEventPayload{ Matches: allMatches.Sorted(), Packages: packages, Context: pkgContext, AppConfig: appConfig, - ImageName: sbom.Source.ImageMetadata.UserInput, - ImageDigest: sbom.Source.ImageMetadata.ManifestDigest, + EventSource: eventSource, ImageVerified: imageVerified, Sbom: base64.StdEncoding.EncodeToString(buf.Bytes()), }); err != nil { diff --git a/internal/xeolio/request.go b/internal/xeolio/request.go index a8189295..81423dc3 100644 --- a/internal/xeolio/request.go +++ b/internal/xeolio/request.go @@ -14,8 +14,7 @@ import ( ) const ( - XeolAPIURL = "https://api.xeol.io" - XeolEngineURL = "https://engine.xeol.io" + XeolAPIURL = "https://api.xeol.io" ) type XeolClient struct { @@ -77,5 +76,5 @@ func (x *XeolClient) SendEvent(payload report.XeolEventPayload) error { return fmt.Errorf("error marshalling xeol.io API request: %v", err) } - return x.makeRequest("PUT", XeolEngineURL, "v1/scan", bytes.NewBuffer(p), nil) + return x.makeRequest("PUT", XeolAPIURL, "v2/scan", bytes.NewBuffer(p), nil) } diff --git a/internal/xeolio/source.go b/internal/xeolio/source.go new file mode 100644 index 00000000..5fff5e60 --- /dev/null +++ b/internal/xeolio/source.go @@ -0,0 +1,76 @@ +package xeolio + +import ( + "fmt" + + "github.com/anchore/syft/syft/source" +) + +type EventSource interface { + Serialize() map[string]interface{} +} + +type DirectorySource struct { + ID string + Type string + Target string +} + +func (s *DirectorySource) Serialize() map[string]interface{} { + return map[string]interface{}{ + "ID": s.ID, + "Type": s.Type, + "Target": s.Target, + } +} + +func NewDirectorySource(sbomSource source.Metadata) *DirectorySource { + return &DirectorySource{ + ID: sbomSource.ID, + Type: string(sbomSource.Scheme), + Target: sbomSource.Path, + } +} + +type ImageSource struct { + ID string + Type string + ImageName string + ImageDigest string + ManifestDigest string +} + +func NewImageSource(sbomSource source.Metadata) *ImageSource { + return &ImageSource{ + ID: sbomSource.ID, + Type: string(sbomSource.Scheme), + ImageName: sbomSource.ImageMetadata.UserInput, + ImageDigest: sbomSource.ImageMetadata.ID, + ManifestDigest: sbomSource.ImageMetadata.ManifestDigest, + } +} + +func (s *ImageSource) Serialize() map[string]interface{} { + return map[string]interface{}{ + "ID": s.ID, + "Type": s.Type, + "ImageName": s.ImageName, + "ImageDigest": s.ImageDigest, + "ManifestDigest": s.ManifestDigest, + } +} + +func EventSourceScheme(sbomSource source.Metadata) source.Scheme { + return sbomSource.Scheme +} + +func NewEventSource(sbomSource source.Metadata) (map[string]interface{}, error) { + if sbomSource.Scheme == source.DirectoryScheme { + return NewDirectorySource(sbomSource).Serialize(), nil + } + if sbomSource.Scheme == source.ImageScheme { + return NewImageSource(sbomSource).Serialize(), nil + } + + return nil, fmt.Errorf("unsupported source type: %s", sbomSource.Scheme) +} diff --git a/xeol/report/model.go b/xeol/report/model.go index 932f301d..5e679371 100644 --- a/xeol/report/model.go +++ b/xeol/report/model.go @@ -11,7 +11,6 @@ type XeolEventPayload struct { Context pkg.Context AppConfig interface{} ImageVerified bool - ImageName string - ImageDigest string + EventSource map[string]interface{} Sbom string }