diff --git a/errors/errors.go b/errors/errors.go index c12698de..fcab3604 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -44,6 +44,7 @@ var ( ErrImgStoreNotFound = errors.New("routes: image store not found corresponding to given route") ErrEmptyValue = errors.New("cache: empty value") ErrEmptyRepoList = errors.New("search: no repository found") + ErrCVESearchDisabled = errors.New("search: CVE search is disabled") ErrInvalidRepositoryName = errors.New("routes: not a repository name") ErrSyncMissingCatalog = errors.New("sync: couldn't fetch upstream registry's catalog") ErrMethodNotSupported = errors.New("storage: method not supported") diff --git a/pkg/extensions/search/cve/cve_test.go b/pkg/extensions/search/cve/cve_test.go index 829c1374..6c8ae633 100644 --- a/pkg/extensions/search/cve/cve_test.go +++ b/pkg/extensions/search/cve/cve_test.go @@ -43,6 +43,7 @@ import ( const ( username = "test" passphrase = "test" + testDir = "../../../../test/data" ) type CveResult struct { @@ -375,6 +376,87 @@ func TestImageFormat(t *testing.T) { }) } +func TestCVESearchDisabled(t *testing.T) { + Convey("Test with CVE search disabled", t, func() { + dbDir := testDir + + port := GetFreePort() + baseURL := GetBaseURL(port) + conf := config.New() + conf.HTTP.Port = port + htpasswdPath := MakeHtpasswdFile() + defer os.Remove(htpasswdPath) + + conf.HTTP.Auth = &config.AuthConfig{ + HTPasswd: config.AuthHTPasswd{ + Path: htpasswdPath, + }, + } + + conf.Storage.RootDirectory = dbDir + defaultVal := true + searchConfig := &extconf.SearchConfig{ + BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, + } + conf.Extensions = &extconf.ExtensionConfig{ + Search: searchConfig, + } + + logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt") + if err != nil { + panic(err) + } + + logPath := logFile.Name() + defer os.Remove(logPath) + + writers := io.MultiWriter(os.Stdout, logFile) + + ctlr := api.NewController(conf) + ctlr.Log.Logger = ctlr.Log.Output(writers) + + go func() { + // this blocks + if err := ctlr.Run(context.Background()); err != nil { + return + } + }() + + // wait till ready + for { + _, err := resty.R().Get(baseURL) + if err == nil { + break + } + time.Sleep(100 * time.Millisecond) + } + + // Wait for trivy db to download + _, err = ReadLogFileAndSearchString(logPath, "DB update completed, next update scheduled", 90*time.Second) + if err != nil { + panic(err) + } + + defer func() { + ctx := context.Background() + _ = ctlr.Server.Shutdown(ctx) + }() + + resp, _ := resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.FullSearchPrefix + "?query={CVEListForImage(image:\"zot-test\"){Tag%20CVEList{Id%20Description%20Severity%20PackageList{Name%20InstalledVersion%20FixedVersion}}}}") + So(string(resp.Body()), ShouldContainSubstring, "search: CVE search is disabled") + So(resp.StatusCode(), ShouldEqual, 200) + + resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListForCVE(id:\"CVE-201-20482\"){RepoName%20Tag}}") + So(string(resp.Body()), ShouldContainSubstring, "search: CVE search is disabled") + So(resp.StatusCode(), ShouldEqual, 200) + + resp, _ = resty.R().SetBasicAuth(username, passphrase).Get(baseURL + constants.FullSearchPrefix + "?query={ImageListWithCVEFixed(id:\"" + "randomId" + "\",image:\"zot-test\"){RepoName%20LastUpdated}}") + So(resp, ShouldNotBeNil) + So(string(resp.Body()), ShouldContainSubstring, "search: CVE search is disabled") + So(resp.StatusCode(), ShouldEqual, 200) + }) +} + func TestCVESearch(t *testing.T) { Convey("Test image vulnerability scanning", t, func() { updateDuration, _ := time.ParseDuration("1h") diff --git a/pkg/extensions/search/schema.resolvers.go b/pkg/extensions/search/schema.resolvers.go index a933da7c..26f911b1 100644 --- a/pkg/extensions/search/schema.resolvers.go +++ b/pkg/extensions/search/schema.resolvers.go @@ -8,6 +8,7 @@ import ( "context" "github.com/vektah/gqlparser/v2/gqlerror" + zerr "zotregistry.io/zot/errors" "zotregistry.io/zot/pkg/extensions/search/common" "zotregistry.io/zot/pkg/extensions/search/convert" "zotregistry.io/zot/pkg/extensions/search/gql_generated" @@ -15,6 +16,10 @@ import ( // CVEListForImage is the resolver for the CVEListForImage field. func (r *queryResolver) CVEListForImage(ctx context.Context, image string) (*gql_generated.CVEResultForImage, error) { + if r.cveInfo == nil { + return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled + } + _, copyImgTag := common.GetImageDirAndTag(image) if copyImgTag == "" { @@ -67,6 +72,10 @@ func (r *queryResolver) ImageListForCve(ctx context.Context, id string) ([]*gql_ olu := common.NewBaseOciLayoutUtils(r.storeController, r.log) affectedImages := []*gql_generated.ImageSummary{} + if r.cveInfo == nil { + return affectedImages, zerr.ErrCVESearchDisabled + } + r.log.Info().Msg("extracting repositories") repoList, err := olu.GetRepositories() if err != nil { //nolint: wsl @@ -119,6 +128,10 @@ func (r *queryResolver) ImageListWithCVEFixed(ctx context.Context, id string, im unaffectedImages := []*gql_generated.ImageSummary{} + if r.cveInfo == nil { + return unaffectedImages, zerr.ErrCVESearchDisabled + } + tagsInfo, err := r.cveInfo.GetImageListWithCVEFixed(image, id) if err != nil { return unaffectedImages, err