//go:build lint // +build lint package lint_test import ( "context" "encoding/json" "fmt" "net/http" "os" "path" "testing" godigest "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" . "github.com/smartystreets/goconvey/convey" "gopkg.in/resty.v1" "zotregistry.io/zot/pkg/api" "zotregistry.io/zot/pkg/api/config" extconf "zotregistry.io/zot/pkg/extensions/config" "zotregistry.io/zot/pkg/extensions/lint" "zotregistry.io/zot/pkg/extensions/monitoring" "zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/storage/local" "zotregistry.io/zot/pkg/test" ) const ( username = "test" passphrase = "test" ServerCert = "../../test/data/server.cert" ServerKey = "../../test/data/server.key" CACert = "../../test/data/ca.crt" AuthorizedNamespace = "everyone/isallowed" UnauthorizedNamespace = "fortknox/notallowed" ALICE = "alice" AuthorizationNamespace = "authz/image" AuthorizationAllRepos = "**" ) func TestVerifyMandatoryAnnotations(t *testing.T) { //nolint: dupl Convey("Mandatory annotations disabled", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := false conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusCreated) }) //nolint: dupl Convey("Mandatory annotations enabled, but no list in config", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusCreated) }) Convey("Mandatory annotations verification passing", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"} ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testPass1" manifest.Annotations["annotation2"] = "testPass2" manifest.Annotations["annotation3"] = "testPass3" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusCreated) }) Convey("Mandatory annotations verification in manifest and config passing", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"} ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "annotationPass1" manifest.Annotations["annotation2"] = "annotationPass2" configDigest := manifest.Config.Digest resp, err = resty.R().SetBasicAuth(username, passphrase). Get(baseURL + fmt.Sprintf("/v2/zot-test/blobs/%s", configDigest)) So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) configBlob := resp.Body() var imageConfig ispec.Image err = json.Unmarshal(configBlob, &imageConfig) So(err, ShouldBeNil) imageConfig.Config.Labels = make(map[string]string) imageConfig.Config.Labels["annotation3"] = "annotationPass3" configContent, err := json.Marshal(imageConfig) So(err, ShouldBeNil) configBlobDigestRaw := godigest.FromBytes(configContent) manifest.Config.Digest = configBlobDigestRaw manifest.Config.Size = int64(len(configContent)) manifestContent, err := json.Marshal(manifest) So(err, ShouldBeNil) // upload image config blob resp, err = resty.R(). Post(fmt.Sprintf("%s/v2/zot-test/blobs/uploads/", baseURL)) So(err, ShouldBeNil) loc := test.Location(baseURL, resp) _, err = resty.R(). SetContentLength(true). SetHeader("Content-Length", fmt.Sprintf("%d", len(configContent))). SetHeader("Content-Type", "application/octet-stream"). SetQueryParam("digest", configBlobDigestRaw.String()). SetBody(configContent). Put(loc) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(manifestContent).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusCreated) }) Convey("Mandatory annotations verification in manifest and config failing", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"} ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testFail1" configDigest := manifest.Config.Digest resp, err = resty.R().SetBasicAuth(username, passphrase). Get(baseURL + fmt.Sprintf("/v2/zot-test/blobs/%s", configDigest)) So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) configBlob := resp.Body() var imageConfig ispec.Image err = json.Unmarshal(configBlob, &imageConfig) So(err, ShouldBeNil) imageConfig.Config.Labels = make(map[string]string) imageConfig.Config.Labels["annotation2"] = "testFail2" configContent, err := json.Marshal(imageConfig) So(err, ShouldBeNil) configBlobDigestRaw := godigest.FromBytes(configContent) manifest.Config.Digest = configBlobDigestRaw manifest.Config.Size = int64(len(configContent)) manifestContent, err := json.Marshal(manifest) So(err, ShouldBeNil) // upload image config blob _, err = resty.R(). Post(fmt.Sprintf("%s/v2/zot-test/blobs/uploads/", baseURL)) So(err, ShouldBeNil) loc := test.Location(baseURL, resp) _, err = resty.R(). SetContentLength(true). SetHeader("Content-Length", fmt.Sprintf("%d", len(configContent))). SetHeader("Content-Type", "application/octet-stream"). SetQueryParam("digest", configBlobDigestRaw.String()). SetBody(configContent). Put(loc) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(manifestContent).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest) }) Convey("Mandatory annotations incomplete in manifest", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"} ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testFail1" manifest.Annotations["annotation3"] = "testFail3" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest) }) Convey("Mandatory annotations verification passing - more annotations than the mandatory list", t, func() { port := test.GetFreePort() baseURL := test.GetBaseURL(port) conf := config.New() conf.HTTP.Port = port enable := true conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}} conf.Extensions.Lint.MandatoryAnnotations = []string{} conf.Extensions.Lint.Enable = &enable conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"} ctlr := api.NewController(conf) dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } ctlr.Config.Storage.RootDirectory = dir go startServer(ctlr) defer stopServer(ctlr) test.WaitTillServerReady(baseURL) resp, err := resty.R().SetBasicAuth(username, passphrase). Get(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) manifestBlob := resp.Body() var manifest ispec.Manifest err = json.Unmarshal(manifestBlob, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testPassMore1" manifest.Annotations["annotation2"] = "testPassMore2" manifest.Annotations["annotation3"] = "testPassMore3" manifest.Annotations["annotation4"] = "testPassMore4" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1") So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusCreated) }) } func TestVerifyMandatoryAnnotationsFunction(t *testing.T) { Convey("Mandatory annotations disabled", t, func() { enable := false lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) indexContent, err := imgStore.GetIndexContent("zot-test") So(err, ShouldBeNil) err = json.Unmarshal(indexContent, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest pass, err := linter.CheckMandatoryAnnotations("zot-test", manifestDigest, imgStore) So(err, ShouldBeNil) So(pass, ShouldBeTrue) }) Convey("Mandatory annotations enabled, but no list in config", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) indexContent, err := imgStore.GetIndexContent("zot-test") So(err, ShouldBeNil) err = json.Unmarshal(indexContent, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest pass, err := linter.CheckMandatoryAnnotations("zot-test", manifestDigest, imgStore) So(err, ShouldBeNil) So(pass, ShouldBeTrue) }) Convey("Mandatory annotations verification passing", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json")) So(err, ShouldBeNil) err = json.Unmarshal(buf, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest var manifest ispec.Manifest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", manifestDigest.Algorithm().String(), manifestDigest.Encoded())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testPass1" manifest.Annotations["annotation2"] = "testPass2" manifest.Annotations["annotation3"] = "testPass3" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) So(content, ShouldNotBeNil) digest := godigest.FromBytes(content) So(digest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), content, 0o600) So(err, ShouldBeNil) manifestDesc := ispec.Descriptor{ Size: int64(len(content)), Digest: digest, } index.Manifests = append(index.Manifests, manifestDesc) linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore) So(err, ShouldBeNil) So(pass, ShouldBeTrue) }) Convey("Mandatory annotations incomplete in manifest", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json")) So(err, ShouldBeNil) err = json.Unmarshal(buf, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest var manifest ispec.Manifest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", manifestDigest.Algorithm().String(), manifestDigest.Encoded())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "test1" manifest.Annotations["annotation3"] = "test3" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) So(content, ShouldNotBeNil) digest := godigest.FromBytes(content) So(digest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), content, 0o600) So(err, ShouldBeNil) manifestDesc := ispec.Descriptor{ Size: int64(len(content)), Digest: digest, } index.Manifests = append(index.Manifests, manifestDesc) linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore) So(err, ShouldBeNil) So(pass, ShouldBeFalse) }) Convey("Mandatory annotations verification passing - more annotations than the mandatory list", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json")) So(err, ShouldBeNil) err = json.Unmarshal(buf, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest var manifest ispec.Manifest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", manifestDigest.Algorithm().String(), manifestDigest.Encoded())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testPassMore1" manifest.Annotations["annotation2"] = "testPassMore2" manifest.Annotations["annotation3"] = "testPassMore3" manifest.Annotations["annotation4"] = "testPassMore4" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) So(content, ShouldNotBeNil) digest := godigest.FromBytes(content) So(digest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), content, 0o600) So(err, ShouldBeNil) manifestDesc := ispec.Descriptor{ Size: int64(len(content)), Digest: digest, } index.Manifests = append(index.Manifests, manifestDesc) linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore) So(err, ShouldBeNil) So(pass, ShouldBeTrue) }) Convey("Cannot unmarshal manifest", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json")) So(err, ShouldBeNil) err = json.Unmarshal(buf, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest var manifest ispec.Manifest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", manifestDigest.Algorithm().String(), manifestDigest.Encoded())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testUnmarshal1" manifest.Annotations["annotation2"] = "testUnmarshal2" manifest.Annotations["annotation3"] = "testUnmarshal3" manifest.SchemaVersion = 2 content, err := json.Marshal(manifest) So(err, ShouldBeNil) So(content, ShouldNotBeNil) digest := godigest.FromBytes(content) So(digest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), content, 0o600) So(err, ShouldBeNil) manifestDesc := ispec.Descriptor{ Size: int64(len(content)), Digest: digest, } index.Manifests = append(index.Manifests, manifestDesc) linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o000) if err != nil { panic(err) } pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore) So(err, ShouldNotBeNil) So(pass, ShouldBeFalse) err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o755) if err != nil { panic(err) } }) Convey("Cannot get config file", t, func() { enable := true lintConfig := &extconf.LintConfig{ BaseConfig: extconf.BaseConfig{Enable: &enable}, MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"}, } dir := t.TempDir() err := test.CopyFiles("../../../test/data", dir) if err != nil { panic(err) } var index ispec.Index buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json")) So(err, ShouldBeNil) err = json.Unmarshal(buf, &index) So(err, ShouldBeNil) manifestDigest := index.Manifests[0].Digest var manifest ispec.Manifest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", manifestDigest.Algorithm().String(), manifestDigest.Encoded())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &manifest) So(err, ShouldBeNil) manifest.Annotations = make(map[string]string) manifest.Annotations["annotation1"] = "testAnnotation1" manifest.Annotations["annotation2"] = "testAnnotation2" // write config var imageConfig ispec.Image configDigest := manifest.Config.Digest buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", "sha256", configDigest.Hex())) So(err, ShouldBeNil) err = json.Unmarshal(buf, &imageConfig) So(err, ShouldBeNil) imageConfig.Config.Labels = make(map[string]string) imageConfig.Config.Labels["annotation3"] = "testAnnotation3" configContent, err := json.Marshal(imageConfig) So(err, ShouldBeNil) So(configContent, ShouldNotBeNil) cfgDigest := godigest.FromBytes(configContent) So(cfgDigest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", "sha256", cfgDigest.Hex()), configContent, 0o600) So(err, ShouldBeNil) // write manifest manifest.SchemaVersion = 2 manifest.Config.Size = int64(len(configContent)) manifest.Config.Digest = cfgDigest manifestContent, err := json.Marshal(manifest) So(err, ShouldBeNil) So(manifestContent, ShouldNotBeNil) digest := godigest.FromBytes(manifestContent) So(digest, ShouldNotBeNil) err = os.WriteFile(path.Join(dir, "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), manifestContent, 0o600) So(err, ShouldBeNil) manifestDesc := ispec.Descriptor{ Size: int64(len(manifestContent)), Digest: digest, } index.Manifests = append(index.Manifests, manifestDesc) linter := lint.NewLinter(lintConfig, log.NewLogger("debug", "")) imgStore := local.NewImageStore(dir, false, 0, false, false, log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter) err = os.Chmod(path.Join(dir, "zot-test", "blobs", "sha256", manifest.Config.Digest.Hex()), 0o000) if err != nil { panic(err) } pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore) So(err, ShouldNotBeNil) So(pass, ShouldBeFalse) err = os.Chmod(path.Join(dir, "zot-test", "blobs", "sha256", manifest.Config.Digest.Hex()), 0o755) if err != nil { panic(err) } }) } func startServer(c *api.Controller) { // this blocks ctx := context.Background() if err := c.Run(ctx); err != nil { return } } func stopServer(c *api.Controller) { ctx := context.Background() _ = c.Server.Shutdown(ctx) }