Skip to content

Commit e68e23c

Browse files
committed
feat: allow disabling CVE independently from search
fixes issue #4096 Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
1 parent a2d738c commit e68e23c

6 files changed

Lines changed: 219 additions & 37 deletions

File tree

pkg/cli/server/extensions_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,41 @@ func TestVerifyExtensionsConfig(t *testing.T) {
100100
So(cli.NewServerRootCmd().Execute(), ShouldNotBeNil)
101101
})
102102

103+
Convey("Test verify CVE can be explicitly disabled for remote storage", t, func(c C) {
104+
content := fmt.Sprintf(`{
105+
"storage":{
106+
"rootDirectory":"%s",
107+
"dedupe":true,
108+
"remoteCache":false,
109+
"storageDriver":{
110+
"name":"s3",
111+
"rootdirectory":"/zot",
112+
"region":"us-east-2",
113+
"bucket":"zot-storage",
114+
"secure":true,
115+
"skipverify":false
116+
}
117+
},
118+
"http":{
119+
"address":"127.0.0.1",
120+
"port":"8080"
121+
},
122+
"extensions":{
123+
"search": {
124+
"enable": true,
125+
"cve": {
126+
"enable": false
127+
}
128+
}
129+
}
130+
}`, t.TempDir())
131+
132+
tmpfile := MakeTempFileWithContent(t, "zot-test.json", content)
133+
os.Args = []string{"cli_test", "verify", tmpfile}
134+
135+
So(cli.NewServerRootCmd().Execute(), ShouldBeNil)
136+
})
137+
103138
Convey("Test verify w/ sync and w/o filesystem storage", t, func(c C) {
104139
content := fmt.Sprintf(`{"storage":{"rootDirectory":"%s", "storageDriver": {"name": "s3"}},
105140
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
@@ -1099,6 +1134,41 @@ func TestServeSearchEnabledNoCVE(t *testing.T) {
10991134
So(found, ShouldBeTrue)
11001135
So(err, ShouldBeNil)
11011136
})
1137+
1138+
Convey("search explicitly enabled, and CVE explicitly disabled", t, func(c C) {
1139+
content := `{
1140+
"storage": {
1141+
"rootDirectory": "%s"
1142+
},
1143+
"http": {
1144+
"address": "127.0.0.1",
1145+
"port": "%s"
1146+
},
1147+
"log": {
1148+
"level": "debug",
1149+
"output": "%s"
1150+
},
1151+
"extensions": {
1152+
"search": {
1153+
"enable": true,
1154+
"cve": {
1155+
"enable": false
1156+
}
1157+
}
1158+
}
1159+
}`
1160+
1161+
logPath, _, err := runCLIWithConfig(t, content)
1162+
So(err, ShouldBeNil)
1163+
1164+
found, err := ReadLogFileAndSearchString(logPath, `"CVE":{"Enable":false`, readLogFileTimeout)
1165+
So(found, ShouldBeTrue)
1166+
So(err, ShouldBeNil)
1167+
1168+
found, err = ReadLogFileAndSearchString(logPath, "updating cve-db", readLogFileTimeout)
1169+
So(found, ShouldBeFalse)
1170+
So(err, ShouldBeNil)
1171+
})
11021172
}
11031173

11041174
func TestServeSearchDisabled(t *testing.T) {

pkg/cli/server/root.go

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -874,41 +874,43 @@ func applyDefaultValues(config *config.Config, viperInstance *viper.Viper, logge
874874
}
875875

876876
if *config.Extensions.Search.Enable && config.Extensions.Search.CVE != nil {
877-
defaultUpdateInterval, _ := time.ParseDuration("2h")
877+
if config.Extensions.Search.CVE.Enable == nil || *config.Extensions.Search.CVE.Enable {
878+
defaultUpdateInterval, _ := time.ParseDuration("2h")
878879

879-
if config.Extensions.Search.CVE.UpdateInterval < defaultUpdateInterval {
880-
config.Extensions.Search.CVE.UpdateInterval = defaultUpdateInterval
880+
if config.Extensions.Search.CVE.UpdateInterval < defaultUpdateInterval {
881+
config.Extensions.Search.CVE.UpdateInterval = defaultUpdateInterval
881882

882-
logger.Warn().Msg("cve update interval set to too-short interval < 2h, " +
883-
"changing update duration to 2 hours and continuing.")
884-
}
883+
logger.Warn().Msg("cve update interval set to too-short interval < 2h, " +
884+
"changing update duration to 2 hours and continuing.")
885+
}
885886

886-
if config.Extensions.Search.CVE.Trivy == nil {
887-
config.Extensions.Search.CVE.Trivy = &extconf.TrivyConfig{}
888-
}
887+
if config.Extensions.Search.CVE.Trivy == nil {
888+
config.Extensions.Search.CVE.Trivy = &extconf.TrivyConfig{}
889+
}
889890

890-
if config.Extensions.Search.CVE.Trivy.DBRepository == "" {
891-
defaultDBDownloadURL := "ghcr.io/aquasecurity/trivy-db"
892-
logger.Info().Str("url", defaultDBDownloadURL).Str("component", "config").
893-
Msg("using default trivy-db download URL.")
891+
if config.Extensions.Search.CVE.Trivy.DBRepository == "" {
892+
defaultDBDownloadURL := "ghcr.io/aquasecurity/trivy-db"
893+
logger.Info().Str("url", defaultDBDownloadURL).Str("component", "config").
894+
Msg("using default trivy-db download URL.")
894895

895-
config.Extensions.Search.CVE.Trivy.DBRepository = defaultDBDownloadURL
896-
}
896+
config.Extensions.Search.CVE.Trivy.DBRepository = defaultDBDownloadURL
897+
}
897898

898-
if config.Extensions.Search.CVE.Trivy.JavaDBRepository == "" {
899-
defaultJavaDBDownloadURL := "ghcr.io/aquasecurity/trivy-java-db"
900-
logger.Info().Str("url", defaultJavaDBDownloadURL).Str("component", "config").
901-
Msg("using default trivy-java-db download URL.")
899+
if config.Extensions.Search.CVE.Trivy.JavaDBRepository == "" {
900+
defaultJavaDBDownloadURL := "ghcr.io/aquasecurity/trivy-java-db"
901+
logger.Info().Str("url", defaultJavaDBDownloadURL).Str("component", "config").
902+
Msg("using default trivy-java-db download URL.")
902903

903-
config.Extensions.Search.CVE.Trivy.JavaDBRepository = defaultJavaDBDownloadURL
904-
}
904+
config.Extensions.Search.CVE.Trivy.JavaDBRepository = defaultJavaDBDownloadURL
905+
}
905906

906-
if len(config.Extensions.Search.CVE.Trivy.VulnSeveritySources) == 0 {
907-
defaultVulnSeveritySources := []string{"auto"}
908-
logger.Info().Strs("vulnSeveritySources", defaultVulnSeveritySources).Str("component", "config").
909-
Msg("using default trivy vulnerability severity sources.")
907+
if len(config.Extensions.Search.CVE.Trivy.VulnSeveritySources) == 0 {
908+
defaultVulnSeveritySources := []string{"auto"}
909+
logger.Info().Strs("vulnSeveritySources", defaultVulnSeveritySources).Str("component", "config").
910+
Msg("using default trivy vulnerability severity sources.")
910911

911-
config.Extensions.Search.CVE.Trivy.VulnSeveritySources = defaultVulnSeveritySources
912+
config.Extensions.Search.CVE.Trivy.VulnSeveritySources = defaultVulnSeveritySources
913+
}
912914
}
913915
}
914916
}

pkg/extensions/config/config.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ type SearchConfig struct {
5353
}
5454

5555
type CVEConfig struct {
56+
// Enable controls whether CVE scanning is active for search.
57+
// If omitted, this field defaults to enabled when the CVE config block is present and search is enabled.
58+
Enable *bool `mapstructure:",omitempty" json:",omitempty"`
5659
UpdateInterval time.Duration // should be 2 hours or more, if not specified default be kept as 2 hours
5760
Trivy *TrivyConfig
5861
}
@@ -109,8 +112,15 @@ func (e *ExtensionConfig) IsCveScanningEnabled() bool {
109112
return false
110113
}
111114

112-
return e.Search != nil && e.Search.Enable != nil && *e.Search.Enable &&
113-
e.Search.CVE != nil && e.Search.CVE.Trivy != nil
115+
if e.Search == nil || e.Search.Enable == nil || !*e.Search.Enable || e.Search.CVE == nil {
116+
return false
117+
}
118+
119+
if e.Search.CVE.Enable != nil && !*e.Search.CVE.Enable {
120+
return false
121+
}
122+
123+
return e.Search.CVE.Trivy != nil
114124
}
115125

116126
// IsEventRecorderEnabled checks if event recording is enabled in this extensions config.

pkg/extensions/config/config_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ func buildSearchConfigWithCVE(enabled bool) *config.ExtensionConfig {
4444
return ext
4545
}
4646

47+
func buildSearchConfigWithCVEDisabled(enabled bool) *config.ExtensionConfig {
48+
disabled := false
49+
ext := &config.ExtensionConfig{}
50+
ext.Search = &config.SearchConfig{
51+
BaseConfig: config.BaseConfig{
52+
Enable: &enabled,
53+
},
54+
CVE: &config.CVEConfig{
55+
Enable: &disabled,
56+
Trivy: &config.TrivyConfig{},
57+
},
58+
}
59+
60+
return ext
61+
}
62+
4763
func buildEventsConfig(enabled bool) *config.ExtensionConfig {
4864
ext := &config.ExtensionConfig{}
4965
ext.Events = &events.Config{
@@ -302,6 +318,12 @@ func TestExtensionConfig(t *testing.T) {
302318
testMethodWithNilEnable("Search", (*config.ExtensionConfig).IsCveScanningEnabled)
303319
testMethodWithDisabledEnable("Search", (*config.ExtensionConfig).IsCveScanningEnabled, buildSearchConfig)
304320
testMethodWithEnabledEnable("Search", (*config.ExtensionConfig).IsCveScanningEnabled, buildSearchConfigWithCVE)
321+
322+
Convey("Test with search enabled but cve explicitly disabled", func() {
323+
enabled := true
324+
extensionConfig := buildSearchConfigWithCVEDisabled(enabled)
325+
So(extensionConfig.IsCveScanningEnabled(), ShouldBeFalse)
326+
})
305327
})
306328

307329
Convey("Test IsEventRecorderEnabled()", func() {

pkg/extensions/search/resolver_test.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,8 @@ func TestGetReferrers(t *testing.T) {
695695
})
696696
}
697697

698-
func TestQueryResolverErrors(t *testing.T) {
699-
Convey("Errors", t, func() {
698+
func TestQueryResolverErrorsAndEmptyResults(t *testing.T) {
699+
Convey("Errors and empty results", t, func() {
700700
log := log.NewTestLogger()
701701
ctx := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
702702
graphql.DefaultRecover)
@@ -752,9 +752,66 @@ func TestQueryResolverErrors(t *testing.T) {
752752

753753
qr := queryResolver{resolverConfig}
754754

755-
_, err := qr.CVEDiffListForImages(ctx, gql_generated.ImageInput{}, gql_generated.ImageInput{},
755+
result, err := qr.CVEDiffListForImages(ctx, gql_generated.ImageInput{}, gql_generated.ImageInput{},
756756
&gql_generated.PageInput{}, nil, nil)
757-
So(err, ShouldNotBeNil)
757+
So(err, ShouldBeNil)
758+
So(result, ShouldNotBeNil)
759+
So(result.Minuend, ShouldNotBeNil)
760+
So(result.Subtrahend, ShouldNotBeNil)
761+
})
762+
763+
Convey("CVEListForImage nil cveinfo returns empty result", func() {
764+
resolverConfig := NewResolver(
765+
log,
766+
storage.StoreController{DefaultStore: mocks.MockedImageStore{}},
767+
mocks.MetaDBMock{},
768+
nil,
769+
)
770+
771+
qr := queryResolver{resolverConfig}
772+
773+
result, err := qr.CVEListForImage(ctx, "repo1:1.0.0", &gql_generated.PageInput{}, nil, nil, nil)
774+
So(err, ShouldBeNil)
775+
So(result, ShouldNotBeNil)
776+
So(result.Page, ShouldNotBeNil)
777+
So(result.CVEList, ShouldNotBeNil)
778+
So(len(result.CVEList), ShouldEqual, 0)
779+
})
780+
781+
Convey("ImageListForCve nil cveinfo returns empty result", func() {
782+
resolverConfig := NewResolver(
783+
log,
784+
storage.StoreController{DefaultStore: mocks.MockedImageStore{}},
785+
mocks.MetaDBMock{},
786+
nil,
787+
)
788+
789+
qr := queryResolver{resolverConfig}
790+
791+
result, err := qr.ImageListForCve(ctx, "CVE-123", &gql_generated.Filter{}, &gql_generated.PageInput{})
792+
So(err, ShouldBeNil)
793+
So(result, ShouldNotBeNil)
794+
So(result.Page, ShouldNotBeNil)
795+
So(result.Results, ShouldNotBeNil)
796+
So(len(result.Results), ShouldEqual, 0)
797+
})
798+
799+
Convey("ImageListWithCVEFixed nil cveinfo returns empty result", func() {
800+
resolverConfig := NewResolver(
801+
log,
802+
storage.StoreController{DefaultStore: mocks.MockedImageStore{}},
803+
mocks.MetaDBMock{},
804+
nil,
805+
)
806+
807+
qr := queryResolver{resolverConfig}
808+
809+
result, err := qr.ImageListWithCVEFixed(ctx, "CVE-123", "repo1", &gql_generated.Filter{}, &gql_generated.PageInput{})
810+
So(err, ShouldBeNil)
811+
So(result, ShouldNotBeNil)
812+
So(result.Page, ShouldNotBeNil)
813+
So(result.Results, ShouldNotBeNil)
814+
So(len(result.Results), ShouldEqual, 0)
758815
})
759816

760817
Convey("CVEDiffListForImages error", func() {

pkg/extensions/search/schema.resolvers.go

Lines changed: 26 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)