From 3ab7121e1ec336127a82f4f18c66ee45abc28dce Mon Sep 17 00:00:00 2001 From: Matthias Bertschy Date: Mon, 27 May 2024 11:55:46 +0200 Subject: [PATCH] check SBOM size and set status to too large Signed-off-by: Matthias Bertschy --- adapters/v1/syft.go | 15 ++++++++- adapters/v1/syft_test.go | 60 ++++++++++++++++++++-------------- cmd/http/main.go | 2 +- config/config.go | 2 ++ go.mod | 1 + go.sum | 2 ++ repositories/apiserver_test.go | 12 +++---- 7 files changed, 62 insertions(+), 32 deletions(-) diff --git a/adapters/v1/syft.go b/adapters/v1/syft.go index b94c5b70..d291149f 100644 --- a/adapters/v1/syft.go +++ b/adapters/v1/syft.go @@ -13,6 +13,7 @@ import ( helpersv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1/helpers" + "github.com/DmitriyVTitov/size" "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft" @@ -30,15 +31,17 @@ import ( // SyftAdapter implements SBOMCreator from ports using Syft's API type SyftAdapter struct { maxImageSize int64 + maxSBOMSize int scanTimeout time.Duration } var _ ports.SBOMCreator = (*SyftAdapter)(nil) // NewSyftAdapter initializes the SyftAdapter struct -func NewSyftAdapter(scanTimeout time.Duration, maxImageSize int64) *SyftAdapter { +func NewSyftAdapter(scanTimeout time.Duration, maxImageSize int64, maxSBOMSize int) *SyftAdapter { return &SyftAdapter{ maxImageSize: maxImageSize, + maxSBOMSize: maxSBOMSize, scanTimeout: scanTimeout, } } @@ -189,6 +192,16 @@ func (s *SyftAdapter) CreateSBOM(ctx context.Context, name, imageID, imageTag st return domainSBOM, err } + // check the size of the SBOM + if sz := size.Of(syftSBOM); sz > s.maxSBOMSize { + logger.L().Ctx(ctx).Warning("SBOM exceeds size limit", + helpers.Int("maxImageSize", s.maxSBOMSize), + helpers.Int("size", sz), + helpers.String("imageID", imageID)) + domainSBOM.Status = helpersv1.TooLarge + return domainSBOM, nil + } + // mark SBOM as ready domainSBOM.Status = helpersv1.Ready diff --git a/adapters/v1/syft_test.go b/adapters/v1/syft_test.go index 9448c793..5c4c9280 100644 --- a/adapters/v1/syft_test.go +++ b/adapters/v1/syft_test.go @@ -22,15 +22,16 @@ func fileContent(path string) []byte { func Test_syftAdapter_CreateSBOM(t *testing.T) { tests := []struct { - name string - imageID string - imageTag string - format string - maxImageSize int64 - options domain.RegistryOptions - scanTimeout time.Duration - wantErr bool - wantIncomplete bool + name string + imageID string + imageTag string + format string + maxImageSize int64 + maxSBOMSize int + options domain.RegistryOptions + scanTimeout time.Duration + wantErr bool + wantStatus string }{ { name: "empty image produces empty SBOM", @@ -63,18 +64,25 @@ func Test_syftAdapter_CreateSBOM(t *testing.T) { }, }, { - name: "big image produces incomplete SBOM because of maxImageSize", - imageID: "library/alpine@sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501", - format: "null", - maxImageSize: 1, - wantIncomplete: true, + name: "big image produces incomplete SBOM because of maxImageSize", + imageID: "library/alpine@sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501", + format: "null", + maxImageSize: 1, + wantStatus: helpersv1.Incomplete, }, { - name: "big image produces incomplete SBOM because of scanTimeout", - imageID: "library/alpine@sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501", - format: "null", - scanTimeout: 1 * time.Millisecond, - wantIncomplete: true, + name: "big image produces too large SBOM because of maxSBOMSize", + imageID: "library/alpine@sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501", + format: "null", + maxSBOMSize: 1, + wantStatus: helpersv1.TooLarge, + }, + { + name: "big image produces incomplete SBOM because of scanTimeout", + imageID: "library/alpine@sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501", + format: "null", + scanTimeout: 1 * time.Millisecond, + wantStatus: helpersv1.Incomplete, }, { name: "system tests image", @@ -107,18 +115,22 @@ func Test_syftAdapter_CreateSBOM(t *testing.T) { if tt.maxImageSize > 0 { maxImageSize = tt.maxImageSize } + maxSBOMSize := 20 * 1024 * 1024 + if tt.maxSBOMSize > 0 { + maxSBOMSize = tt.maxSBOMSize + } scanTimeout := 5 * time.Minute if tt.scanTimeout > 0 { scanTimeout = tt.scanTimeout } - s := NewSyftAdapter(scanTimeout, maxImageSize) + s := NewSyftAdapter(scanTimeout, maxImageSize, maxSBOMSize) got, err := s.CreateSBOM(context.TODO(), "name", tt.imageID, tt.imageTag, tt.options) if (err != nil) != tt.wantErr { t.Errorf("CreateSBOM() error = %v, wantErr %v", err, tt.wantErr) return } - if tt.wantIncomplete && got.Status != helpersv1.Incomplete { - t.Errorf("CreateSBOM() want incomplete SBOM, got %v", got.Status) + if tt.wantStatus != "" && got.Status != tt.wantStatus { + t.Errorf("CreateSBOM() want %v SBOM, got %v", tt.wantStatus, got.Status) return } content, err := json.Marshal(got.Content) @@ -132,7 +144,7 @@ func Test_syftAdapter_CreateSBOM(t *testing.T) { } func Test_syftAdapter_Version(t *testing.T) { - s := NewSyftAdapter(5*time.Minute, 512*1024*1024) + s := NewSyftAdapter(5*time.Minute, 512*1024*1024, 20*1024*1024) version := s.Version() assert.NotEqual(t, version, "") } @@ -150,7 +162,7 @@ func Test_syftAdapter_transformations(t *testing.T) { sbom := toSyftModel(d) // Convert to domain.sbom - s := NewSyftAdapter(5*time.Minute, 512*1024*1024) + s := NewSyftAdapter(5*time.Minute, 512*1024*1024, 20*1024*1024) domainSBOM, err := s.syftToDomain(*sbom) require.NoError(t, err) diff --git a/cmd/http/main.go b/cmd/http/main.go index b7df0cbc..6d479d21 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -62,7 +62,7 @@ func main() { logger.L().Ctx(ctx).Fatal("storage initialization error", helpers.Error(err)) } } - sbomAdapter := v1.NewSyftAdapter(c.ScanTimeout, c.MaxImageSize) + sbomAdapter := v1.NewSyftAdapter(c.ScanTimeout, c.MaxImageSize, c.MaxSBOMSize) cveAdapter := v1.NewGrypeAdapter(c.ListingURL) var platform ports.Platform if c.KeepLocal { diff --git a/config/config.go b/config/config.go index dc6ded48..dfef9096 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,7 @@ type Config struct { ClusterName string `mapstructure:"clusterName"` ListingURL string `mapstructure:"listingURL"` MaxImageSize int64 `mapstructure:"maxImageSize"` + MaxSBOMSize int `mapstructure:"maxSBOMSize"` ScanConcurrency int `mapstructure:"scanConcurrency"` ScanTimeout time.Duration `mapstructure:"scanTimeout"` KeepLocal bool `mapstructure:"keepLocal"` @@ -31,6 +32,7 @@ func LoadConfig(path string) (Config, error) { viper.SetDefault("listingURL", "https://toolbox-data.anchore.io/grype/databases/listing.json") viper.SetDefault("maxImageSize", 512*1024*1024) + viper.SetDefault("maxSBOMSize", 20*1024*1024) viper.SetDefault("scanConcurrency", 1) viper.SetDefault("scanTimeout", 5*time.Minute) viper.SetDefault("vexGeneration", false) diff --git a/go.mod b/go.mod index fa8e22e5..9d237629 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/kubescape/kubevuln go 1.21.3 require ( + github.com/DmitriyVTitov/size v1.5.0 github.com/adrg/xdg v0.4.0 github.com/akyoto/cache v1.0.6 github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65 diff --git a/go.sum b/go.sum index 766bd042..6972863f 100644 --- a/go.sum +++ b/go.sum @@ -231,6 +231,8 @@ github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7B github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g= +github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= diff --git a/repositories/apiserver_test.go b/repositories/apiserver_test.go index 6e37dbaf..85ed4208 100644 --- a/repositories/apiserver_test.go +++ b/repositories/apiserver_test.go @@ -235,9 +235,9 @@ func TestAPIServerStore_GetSBOM(t *testing.T) { require.NoError(t, err) err = a.StoreSBOM(tt.args.ctx, tt.sbom) require.NoError(t, err) - gotSbom, _ := a.GetSBOM(tt.args.ctx, tt.args.name, tt.args.SBOMCreatorVersion) - if (gotSbom.Content == nil) != tt.wantEmptySBOM { - t.Errorf("GetSBOM() gotSbom.Content = %v, wantEmptySBOM %v", gotSbom.Content, tt.wantEmptySBOM) + gotSBOM, _ := a.GetSBOM(tt.args.ctx, tt.args.name, tt.args.SBOMCreatorVersion) + if (gotSBOM.Content == nil) != tt.wantEmptySBOM { + t.Errorf("GetSBOM() gotSBOM.Content = %v, wantEmptySBOM %v", gotSBOM.Content, tt.wantEmptySBOM) return } }) @@ -337,10 +337,10 @@ func TestAPIServerStore_GetSBOMp(t *testing.T) { require.NoError(t, err) err = a.storeSBOMp(tt.args.ctx, tt.sbom, tt.incomplete) require.NoError(t, err) - gotSbom, _ := a.GetSBOMp(tt.args.ctx, tt.args.name, tt.args.SBOMCreatorVersion) + gotSBOM, _ := a.GetSBOMp(tt.args.ctx, tt.args.name, tt.args.SBOMCreatorVersion) if !tt.wantEmptySBOM { - assert.NotNil(t, gotSbom.Content) - assert.Equal(t, "bar", gotSbom.Annotations["foo"]) + assert.NotNil(t, gotSBOM.Content) + assert.Equal(t, "bar", gotSBOM.Annotations["foo"]) } }) }