zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/lint/lint_test.go (about)

     1  //go:build lint
     2  // +build lint
     3  
     4  package lint_test
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"path"
    12  	"testing"
    13  
    14  	godigest "github.com/opencontainers/go-digest"
    15  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    16  	. "github.com/smartystreets/goconvey/convey"
    17  	"gopkg.in/resty.v1"
    18  
    19  	"zotregistry.dev/zot/pkg/api"
    20  	"zotregistry.dev/zot/pkg/api/config"
    21  	extconf "zotregistry.dev/zot/pkg/extensions/config"
    22  	"zotregistry.dev/zot/pkg/extensions/lint"
    23  	"zotregistry.dev/zot/pkg/extensions/monitoring"
    24  	"zotregistry.dev/zot/pkg/log"
    25  	"zotregistry.dev/zot/pkg/storage/local"
    26  	test "zotregistry.dev/zot/pkg/test/common"
    27  	. "zotregistry.dev/zot/pkg/test/image-utils"
    28  	ociutils "zotregistry.dev/zot/pkg/test/oci-utils"
    29  )
    30  
    31  func TestVerifyMandatoryAnnotations(t *testing.T) {
    32  	//nolint: dupl
    33  	Convey("Mandatory annotations disabled", t, func() {
    34  		port := test.GetFreePort()
    35  		baseURL := test.GetBaseURL(port)
    36  
    37  		conf := config.New()
    38  		conf.HTTP.Port = port
    39  		enable := false
    40  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
    41  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
    42  		conf.Extensions.Lint.Enable = &enable
    43  
    44  		ctlr := api.NewController(conf)
    45  		dir := t.TempDir()
    46  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
    47  
    48  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
    49  		So(err, ShouldBeNil)
    50  
    51  		ctlr.Config.Storage.RootDirectory = dir
    52  
    53  		cm := test.NewControllerManager(ctlr)
    54  		cm.StartAndWait(port)
    55  		defer cm.StopServer()
    56  
    57  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
    58  		So(err, ShouldBeNil)
    59  		So(resp, ShouldNotBeNil)
    60  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
    61  
    62  		manifestBlob := resp.Body()
    63  		var manifest ispec.Manifest
    64  		err = json.Unmarshal(manifestBlob, &manifest)
    65  		So(err, ShouldBeNil)
    66  
    67  		manifest.SchemaVersion = 2
    68  		content, err := json.Marshal(manifest)
    69  		So(err, ShouldBeNil)
    70  
    71  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
    72  			SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
    73  		So(err, ShouldBeNil)
    74  		So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
    75  	})
    76  
    77  	//nolint: dupl
    78  	Convey("Mandatory annotations enabled, but no list in config", t, func() {
    79  		port := test.GetFreePort()
    80  		baseURL := test.GetBaseURL(port)
    81  
    82  		conf := config.New()
    83  		conf.HTTP.Port = port
    84  		enable := true
    85  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
    86  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
    87  
    88  		conf.Extensions.Lint.Enable = &enable
    89  
    90  		ctlr := api.NewController(conf)
    91  		dir := t.TempDir()
    92  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
    93  
    94  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
    95  		So(err, ShouldBeNil)
    96  
    97  		ctlr.Config.Storage.RootDirectory = dir
    98  
    99  		cm := test.NewControllerManager(ctlr)
   100  		cm.StartAndWait(port)
   101  		defer cm.StopServer()
   102  
   103  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   104  		So(err, ShouldBeNil)
   105  		So(resp, ShouldNotBeNil)
   106  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   107  
   108  		manifestBlob := resp.Body()
   109  		var manifest ispec.Manifest
   110  		err = json.Unmarshal(manifestBlob, &manifest)
   111  		So(err, ShouldBeNil)
   112  
   113  		manifest.SchemaVersion = 2
   114  		content, err := json.Marshal(manifest)
   115  		So(err, ShouldBeNil)
   116  
   117  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   118  			SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   119  		So(err, ShouldBeNil)
   120  		So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
   121  	})
   122  
   123  	Convey("Mandatory annotations verification passing", t, func() {
   124  		port := test.GetFreePort()
   125  		baseURL := test.GetBaseURL(port)
   126  
   127  		conf := config.New()
   128  		conf.HTTP.Port = port
   129  		enable := true
   130  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
   131  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
   132  
   133  		conf.Extensions.Lint.Enable = &enable
   134  		conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"}
   135  
   136  		ctlr := api.NewController(conf)
   137  		dir := t.TempDir()
   138  
   139  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
   140  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   141  		So(err, ShouldBeNil)
   142  
   143  		ctlr.Config.Storage.RootDirectory = dir
   144  
   145  		cm := test.NewControllerManager(ctlr)
   146  		cm.StartAndWait(port)
   147  		defer cm.StopServer()
   148  
   149  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   150  		So(err, ShouldBeNil)
   151  		So(resp, ShouldNotBeNil)
   152  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   153  
   154  		manifestBlob := resp.Body()
   155  		var manifest ispec.Manifest
   156  		err = json.Unmarshal(manifestBlob, &manifest)
   157  		So(err, ShouldBeNil)
   158  
   159  		manifest.Annotations = make(map[string]string)
   160  
   161  		manifest.Annotations["annotation1"] = "testPass1"
   162  		manifest.Annotations["annotation2"] = "testPass2"
   163  		manifest.Annotations["annotation3"] = "testPass3"
   164  
   165  		manifest.SchemaVersion = 2
   166  		content, err := json.Marshal(manifest)
   167  		So(err, ShouldBeNil)
   168  
   169  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   170  			SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   171  		So(err, ShouldBeNil)
   172  		So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
   173  	})
   174  
   175  	Convey("Mandatory annotations verification in manifest and config passing", t, func() {
   176  		port := test.GetFreePort()
   177  		baseURL := test.GetBaseURL(port)
   178  
   179  		conf := config.New()
   180  		conf.HTTP.Port = port
   181  		enable := true
   182  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
   183  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
   184  
   185  		conf.Extensions.Lint.Enable = &enable
   186  		conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"}
   187  
   188  		ctlr := api.NewController(conf)
   189  		dir := t.TempDir()
   190  
   191  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
   192  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   193  		So(err, ShouldBeNil)
   194  
   195  		ctlr.Config.Storage.RootDirectory = dir
   196  
   197  		cm := test.NewControllerManager(ctlr)
   198  		cm.StartAndWait(port)
   199  		defer cm.StopServer()
   200  
   201  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   202  		So(err, ShouldBeNil)
   203  		So(resp, ShouldNotBeNil)
   204  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   205  
   206  		manifestBlob := resp.Body()
   207  		var manifest ispec.Manifest
   208  		err = json.Unmarshal(manifestBlob, &manifest)
   209  		So(err, ShouldBeNil)
   210  
   211  		manifest.Annotations = make(map[string]string)
   212  
   213  		manifest.Annotations["annotation1"] = "annotationPass1"
   214  		manifest.Annotations["annotation2"] = "annotationPass2"
   215  
   216  		configDigest := manifest.Config.Digest
   217  
   218  		resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/zot-test/blobs/%s", configDigest))
   219  		So(err, ShouldBeNil)
   220  		So(resp, ShouldNotBeNil)
   221  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   222  
   223  		configBlob := resp.Body()
   224  		var imageConfig ispec.Image
   225  		err = json.Unmarshal(configBlob, &imageConfig)
   226  		So(err, ShouldBeNil)
   227  
   228  		imageConfig.Config.Labels = make(map[string]string)
   229  		imageConfig.Config.Labels["annotation3"] = "annotationPass3"
   230  
   231  		configContent, err := json.Marshal(imageConfig)
   232  		So(err, ShouldBeNil)
   233  
   234  		configBlobDigestRaw := godigest.FromBytes(configContent)
   235  		manifest.Config.Digest = configBlobDigestRaw
   236  		manifest.Config.Size = int64(len(configContent))
   237  		manifestContent, err := json.Marshal(manifest)
   238  		So(err, ShouldBeNil)
   239  
   240  		// upload image config blob
   241  		resp, err = resty.R().
   242  			Post(fmt.Sprintf("%s/v2/zot-test/blobs/uploads/", baseURL))
   243  		So(err, ShouldBeNil)
   244  		loc := test.Location(baseURL, resp)
   245  
   246  		_, err = resty.R().
   247  			SetContentLength(true).
   248  			SetHeader("Content-Length", fmt.Sprintf("%d", len(configContent))).
   249  			SetHeader("Content-Type", "application/octet-stream").
   250  			SetQueryParam("digest", configBlobDigestRaw.String()).
   251  			SetBody(configContent).
   252  			Put(loc)
   253  		So(err, ShouldBeNil)
   254  
   255  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   256  			SetBody(manifestContent).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   257  		So(err, ShouldBeNil)
   258  		So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
   259  	})
   260  
   261  	Convey("Mandatory annotations verification in manifest and config failing", t, func() {
   262  		port := test.GetFreePort()
   263  		baseURL := test.GetBaseURL(port)
   264  
   265  		conf := config.New()
   266  		conf.HTTP.Port = port
   267  		enable := true
   268  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
   269  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
   270  
   271  		conf.Extensions.Lint.Enable = &enable
   272  		conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"}
   273  
   274  		ctlr := api.NewController(conf)
   275  		dir := t.TempDir()
   276  
   277  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
   278  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   279  		So(err, ShouldBeNil)
   280  
   281  		ctlr.Config.Storage.RootDirectory = dir
   282  
   283  		cm := test.NewControllerManager(ctlr)
   284  		cm.StartAndWait(port)
   285  		defer cm.StopServer()
   286  
   287  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   288  		So(err, ShouldBeNil)
   289  		So(resp, ShouldNotBeNil)
   290  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   291  
   292  		manifestBlob := resp.Body()
   293  		var manifest ispec.Manifest
   294  		err = json.Unmarshal(manifestBlob, &manifest)
   295  		So(err, ShouldBeNil)
   296  
   297  		manifest.Annotations = make(map[string]string)
   298  
   299  		manifest.Annotations["annotation1"] = "testFail1"
   300  
   301  		configDigest := manifest.Config.Digest
   302  
   303  		resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/zot-test/blobs/%s", configDigest))
   304  		So(err, ShouldBeNil)
   305  		So(resp, ShouldNotBeNil)
   306  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   307  
   308  		configBlob := resp.Body()
   309  		var imageConfig ispec.Image
   310  		err = json.Unmarshal(configBlob, &imageConfig)
   311  		So(err, ShouldBeNil)
   312  
   313  		imageConfig.Config.Labels = make(map[string]string)
   314  		imageConfig.Config.Labels["annotation2"] = "testFail2"
   315  
   316  		configContent, err := json.Marshal(imageConfig)
   317  		So(err, ShouldBeNil)
   318  
   319  		configBlobDigestRaw := godigest.FromBytes(configContent)
   320  		manifest.Config.Digest = configBlobDigestRaw
   321  		manifest.Config.Size = int64(len(configContent))
   322  		manifestContent, err := json.Marshal(manifest)
   323  		So(err, ShouldBeNil)
   324  
   325  		// upload image config blob
   326  		_, err = resty.R().
   327  			Post(fmt.Sprintf("%s/v2/zot-test/blobs/uploads/", baseURL))
   328  		So(err, ShouldBeNil)
   329  		loc := test.Location(baseURL, resp)
   330  
   331  		_, err = resty.R().
   332  			SetContentLength(true).
   333  			SetHeader("Content-Length", fmt.Sprintf("%d", len(configContent))).
   334  			SetHeader("Content-Type", "application/octet-stream").
   335  			SetQueryParam("digest", configBlobDigestRaw.String()).
   336  			SetBody(configContent).
   337  			Put(loc)
   338  		So(err, ShouldBeNil)
   339  
   340  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   341  			SetBody(manifestContent).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   342  		So(err, ShouldBeNil)
   343  		So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
   344  	})
   345  
   346  	Convey("Mandatory annotations incomplete in manifest", t, func() {
   347  		port := test.GetFreePort()
   348  		baseURL := test.GetBaseURL(port)
   349  
   350  		conf := config.New()
   351  		conf.HTTP.Port = port
   352  		enable := true
   353  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
   354  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
   355  
   356  		conf.Extensions.Lint.Enable = &enable
   357  		conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"}
   358  
   359  		ctlr := api.NewController(conf)
   360  		dir := t.TempDir()
   361  
   362  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
   363  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   364  		So(err, ShouldBeNil)
   365  
   366  		ctlr.Config.Storage.RootDirectory = dir
   367  
   368  		cm := test.NewControllerManager(ctlr)
   369  		cm.StartAndWait(port)
   370  		defer cm.StopServer()
   371  
   372  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   373  		So(err, ShouldBeNil)
   374  		So(resp, ShouldNotBeNil)
   375  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   376  
   377  		manifestBlob := resp.Body()
   378  		var manifest ispec.Manifest
   379  		err = json.Unmarshal(manifestBlob, &manifest)
   380  		So(err, ShouldBeNil)
   381  
   382  		manifest.Annotations = make(map[string]string)
   383  
   384  		manifest.Annotations["annotation1"] = "testFail1"
   385  		manifest.Annotations["annotation3"] = "testFail3"
   386  
   387  		manifest.SchemaVersion = 2
   388  		content, err := json.Marshal(manifest)
   389  		So(err, ShouldBeNil)
   390  
   391  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   392  			SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   393  		So(err, ShouldBeNil)
   394  		So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
   395  	})
   396  
   397  	Convey("Mandatory annotations verification passing - more annotations than the mandatory list", t, func() {
   398  		port := test.GetFreePort()
   399  		baseURL := test.GetBaseURL(port)
   400  
   401  		conf := config.New()
   402  		conf.HTTP.Port = port
   403  		enable := true
   404  		conf.Extensions = &extconf.ExtensionConfig{Lint: &extconf.LintConfig{}}
   405  		conf.Extensions.Lint.MandatoryAnnotations = []string{}
   406  		conf.Extensions.Lint.Enable = &enable
   407  		conf.Extensions.Lint.MandatoryAnnotations = []string{"annotation1", "annotation2", "annotation3"}
   408  
   409  		ctlr := api.NewController(conf)
   410  		dir := t.TempDir()
   411  
   412  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, ctlr.Log)
   413  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   414  		So(err, ShouldBeNil)
   415  
   416  		files, err := os.ReadDir(dir)
   417  		So(err, ShouldBeNil)
   418  
   419  		t.Log("Files in dir:", dir, ": ", files)
   420  
   421  		ctlr.Config.Storage.RootDirectory = dir
   422  		cm := test.NewControllerManager(ctlr)
   423  
   424  		cm.StartAndWait(port)
   425  		defer cm.StopServer()
   426  
   427  		resp, err := resty.R().Get(baseURL + "/v2/zot-test/manifests/0.0.1")
   428  		So(err, ShouldBeNil)
   429  		So(resp, ShouldNotBeNil)
   430  		So(resp.StatusCode(), ShouldEqual, http.StatusOK)
   431  
   432  		manifestBlob := resp.Body()
   433  		var manifest ispec.Manifest
   434  		err = json.Unmarshal(manifestBlob, &manifest)
   435  		So(err, ShouldBeNil)
   436  
   437  		manifest.Annotations = make(map[string]string)
   438  
   439  		manifest.Annotations["annotation1"] = "testPassMore1"
   440  		manifest.Annotations["annotation2"] = "testPassMore2"
   441  		manifest.Annotations["annotation3"] = "testPassMore3"
   442  		manifest.Annotations["annotation4"] = "testPassMore4"
   443  
   444  		manifest.SchemaVersion = 2
   445  		content, err := json.Marshal(manifest)
   446  		So(err, ShouldBeNil)
   447  
   448  		resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
   449  			SetBody(content).Put(baseURL + "/v2/zot-test/manifests/0.0.1")
   450  		So(err, ShouldBeNil)
   451  		So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
   452  	})
   453  }
   454  
   455  func TestVerifyMandatoryAnnotationsFunction(t *testing.T) {
   456  	Convey("Mandatory annotations disabled", t, func() {
   457  		enable := false
   458  
   459  		lintConfig := &extconf.LintConfig{
   460  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   461  			MandatoryAnnotations: []string{},
   462  		}
   463  
   464  		dir := t.TempDir()
   465  
   466  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   467  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   468  		So(err, ShouldBeNil)
   469  
   470  		var index ispec.Index
   471  
   472  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   473  		imgStore := local.NewImageStore(dir, false, false,
   474  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   475  
   476  		indexContent, err := imgStore.GetIndexContent("zot-test")
   477  		So(err, ShouldBeNil)
   478  		err = json.Unmarshal(indexContent, &index)
   479  		So(err, ShouldBeNil)
   480  
   481  		manifestDigest := index.Manifests[0].Digest
   482  
   483  		pass, err := linter.CheckMandatoryAnnotations("zot-test", manifestDigest, imgStore)
   484  		So(err, ShouldBeNil)
   485  		So(pass, ShouldBeTrue)
   486  	})
   487  
   488  	Convey("Mandatory annotations enabled, but no list in config", t, func() {
   489  		enable := true
   490  
   491  		lintConfig := &extconf.LintConfig{
   492  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   493  			MandatoryAnnotations: []string{},
   494  		}
   495  
   496  		dir := t.TempDir()
   497  
   498  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   499  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   500  		So(err, ShouldBeNil)
   501  
   502  		var index ispec.Index
   503  
   504  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   505  		imgStore := local.NewImageStore(dir, false, false,
   506  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   507  
   508  		indexContent, err := imgStore.GetIndexContent("zot-test")
   509  		So(err, ShouldBeNil)
   510  		err = json.Unmarshal(indexContent, &index)
   511  		So(err, ShouldBeNil)
   512  
   513  		manifestDigest := index.Manifests[0].Digest
   514  
   515  		pass, err := linter.CheckMandatoryAnnotations("zot-test", manifestDigest, imgStore)
   516  		So(err, ShouldBeNil)
   517  		So(pass, ShouldBeTrue)
   518  	})
   519  
   520  	Convey("Mandatory annotations verification passing", t, func() {
   521  		enable := true
   522  
   523  		lintConfig := &extconf.LintConfig{
   524  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   525  			MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"},
   526  		}
   527  
   528  		dir := t.TempDir()
   529  
   530  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   531  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   532  		So(err, ShouldBeNil)
   533  
   534  		var index ispec.Index
   535  		buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json"))
   536  		So(err, ShouldBeNil)
   537  		err = json.Unmarshal(buf, &index)
   538  		So(err, ShouldBeNil)
   539  
   540  		manifestDigest := index.Manifests[0].Digest
   541  
   542  		var manifest ispec.Manifest
   543  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs",
   544  			manifestDigest.Algorithm().String(), manifestDigest.Encoded()))
   545  		So(err, ShouldBeNil)
   546  		err = json.Unmarshal(buf, &manifest)
   547  		So(err, ShouldBeNil)
   548  
   549  		manifest.Annotations = make(map[string]string)
   550  
   551  		manifest.Annotations["annotation1"] = "testPass1"
   552  		manifest.Annotations["annotation2"] = "testPass2"
   553  		manifest.Annotations["annotation3"] = "testPass3"
   554  
   555  		manifest.SchemaVersion = 2
   556  		content, err := json.Marshal(manifest)
   557  		So(err, ShouldBeNil)
   558  		So(content, ShouldNotBeNil)
   559  
   560  		digest := godigest.FromBytes(content)
   561  		So(digest, ShouldNotBeNil)
   562  
   563  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs",
   564  			digest.Algorithm().String(), digest.Encoded()), content, 0o600)
   565  		So(err, ShouldBeNil)
   566  
   567  		manifestDesc := ispec.Descriptor{
   568  			Size:   int64(len(content)),
   569  			Digest: digest,
   570  		}
   571  
   572  		index.Manifests = append(index.Manifests, manifestDesc)
   573  
   574  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   575  		imgStore := local.NewImageStore(dir, false, false,
   576  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   577  
   578  		pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   579  		So(err, ShouldBeNil)
   580  		So(pass, ShouldBeTrue)
   581  	})
   582  
   583  	Convey("Mandatory annotations incomplete in manifest", t, func() {
   584  		enable := true
   585  
   586  		lintConfig := &extconf.LintConfig{
   587  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   588  			MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"},
   589  		}
   590  
   591  		dir := t.TempDir()
   592  
   593  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   594  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   595  		So(err, ShouldBeNil)
   596  
   597  		var index ispec.Index
   598  		buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json"))
   599  		So(err, ShouldBeNil)
   600  		err = json.Unmarshal(buf, &index)
   601  		So(err, ShouldBeNil)
   602  
   603  		manifestDigest := index.Manifests[0].Digest
   604  
   605  		var manifest ispec.Manifest
   606  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs",
   607  			manifestDigest.Algorithm().String(), manifestDigest.Encoded()))
   608  		So(err, ShouldBeNil)
   609  		err = json.Unmarshal(buf, &manifest)
   610  		So(err, ShouldBeNil)
   611  
   612  		manifest.Annotations = make(map[string]string)
   613  
   614  		manifest.Annotations["annotation1"] = "test1"
   615  		manifest.Annotations["annotation3"] = "test3"
   616  
   617  		manifest.SchemaVersion = 2
   618  		content, err := json.Marshal(manifest)
   619  		So(err, ShouldBeNil)
   620  		So(content, ShouldNotBeNil)
   621  
   622  		digest := godigest.FromBytes(content)
   623  		So(digest, ShouldNotBeNil)
   624  
   625  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs",
   626  			digest.Algorithm().String(), digest.Encoded()), content, 0o600)
   627  		So(err, ShouldBeNil)
   628  
   629  		manifestDesc := ispec.Descriptor{
   630  			Size:   int64(len(content)),
   631  			Digest: digest,
   632  		}
   633  
   634  		index.Manifests = append(index.Manifests, manifestDesc)
   635  
   636  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   637  		imgStore := local.NewImageStore(dir, false, false,
   638  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   639  
   640  		pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   641  		So(err, ShouldNotBeNil)
   642  		So(pass, ShouldBeFalse)
   643  	})
   644  
   645  	Convey("Mandatory annotations verification passing - more annotations than the mandatory list", t, func() {
   646  		enable := true
   647  
   648  		lintConfig := &extconf.LintConfig{
   649  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   650  			MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"},
   651  		}
   652  
   653  		dir := t.TempDir()
   654  
   655  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   656  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   657  		So(err, ShouldBeNil)
   658  
   659  		var index ispec.Index
   660  		buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json"))
   661  		So(err, ShouldBeNil)
   662  		err = json.Unmarshal(buf, &index)
   663  		So(err, ShouldBeNil)
   664  
   665  		manifestDigest := index.Manifests[0].Digest
   666  
   667  		var manifest ispec.Manifest
   668  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs",
   669  			manifestDigest.Algorithm().String(), manifestDigest.Encoded()))
   670  		So(err, ShouldBeNil)
   671  		err = json.Unmarshal(buf, &manifest)
   672  		So(err, ShouldBeNil)
   673  
   674  		manifest.Annotations = make(map[string]string)
   675  
   676  		manifest.Annotations["annotation1"] = "testPassMore1"
   677  		manifest.Annotations["annotation2"] = "testPassMore2"
   678  		manifest.Annotations["annotation3"] = "testPassMore3"
   679  		manifest.Annotations["annotation4"] = "testPassMore4"
   680  
   681  		manifest.SchemaVersion = 2
   682  		content, err := json.Marshal(manifest)
   683  		So(err, ShouldBeNil)
   684  		So(content, ShouldNotBeNil)
   685  
   686  		digest := godigest.FromBytes(content)
   687  		So(digest, ShouldNotBeNil)
   688  
   689  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs",
   690  			digest.Algorithm().String(), digest.Encoded()), content, 0o600)
   691  		So(err, ShouldBeNil)
   692  
   693  		manifestDesc := ispec.Descriptor{
   694  			Size:   int64(len(content)),
   695  			Digest: digest,
   696  		}
   697  
   698  		index.Manifests = append(index.Manifests, manifestDesc)
   699  
   700  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   701  		imgStore := local.NewImageStore(dir, false, false,
   702  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   703  
   704  		pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   705  		So(err, ShouldBeNil)
   706  		So(pass, ShouldBeTrue)
   707  	})
   708  
   709  	Convey("Cannot unmarshal manifest", t, func() {
   710  		enable := true
   711  
   712  		lintConfig := &extconf.LintConfig{
   713  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   714  			MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"},
   715  		}
   716  
   717  		dir := t.TempDir()
   718  
   719  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   720  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   721  		So(err, ShouldBeNil)
   722  
   723  		var index ispec.Index
   724  		buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json"))
   725  		So(err, ShouldBeNil)
   726  		err = json.Unmarshal(buf, &index)
   727  		So(err, ShouldBeNil)
   728  
   729  		manifestDigest := index.Manifests[0].Digest
   730  
   731  		var manifest ispec.Manifest
   732  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs",
   733  			manifestDigest.Algorithm().String(), manifestDigest.Encoded()))
   734  		So(err, ShouldBeNil)
   735  		err = json.Unmarshal(buf, &manifest)
   736  		So(err, ShouldBeNil)
   737  
   738  		manifest.Annotations = make(map[string]string)
   739  
   740  		manifest.Annotations["annotation1"] = "testUnmarshal1"
   741  		manifest.Annotations["annotation2"] = "testUnmarshal2"
   742  		manifest.Annotations["annotation3"] = "testUnmarshal3"
   743  
   744  		manifest.SchemaVersion = 2
   745  		content, err := json.Marshal(manifest)
   746  		So(err, ShouldBeNil)
   747  		So(content, ShouldNotBeNil)
   748  
   749  		digest := godigest.FromBytes(content)
   750  		So(digest, ShouldNotBeNil)
   751  
   752  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs",
   753  			digest.Algorithm().String(), digest.Encoded()), content, 0o600)
   754  		So(err, ShouldBeNil)
   755  
   756  		manifestDesc := ispec.Descriptor{
   757  			Size:   int64(len(content)),
   758  			Digest: digest,
   759  		}
   760  
   761  		index.Manifests = append(index.Manifests, manifestDesc)
   762  
   763  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   764  		imgStore := local.NewImageStore(dir, false, false,
   765  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   766  
   767  		err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o000)
   768  		if err != nil {
   769  			panic(err)
   770  		}
   771  
   772  		pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   773  		So(err, ShouldNotBeNil)
   774  		So(pass, ShouldBeFalse)
   775  
   776  		err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o755)
   777  		if err != nil {
   778  			panic(err)
   779  		}
   780  	})
   781  
   782  	Convey("Cannot get config file", t, func() {
   783  		enable := true
   784  
   785  		lintConfig := &extconf.LintConfig{
   786  			BaseConfig:           extconf.BaseConfig{Enable: &enable},
   787  			MandatoryAnnotations: []string{"annotation1", "annotation2", "annotation3"},
   788  		}
   789  
   790  		dir := t.TempDir()
   791  
   792  		testStoreCtlr := ociutils.GetDefaultStoreController(dir, log.NewLogger("debug", ""))
   793  		err := WriteImageToFileSystem(CreateRandomImage(), "zot-test", "0.0.1", testStoreCtlr)
   794  		So(err, ShouldBeNil)
   795  
   796  		var index ispec.Index
   797  		buf, err := os.ReadFile(path.Join(dir, "zot-test", "index.json"))
   798  		So(err, ShouldBeNil)
   799  		err = json.Unmarshal(buf, &index)
   800  		So(err, ShouldBeNil)
   801  
   802  		manifestDigest := index.Manifests[0].Digest
   803  
   804  		var manifest ispec.Manifest
   805  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs",
   806  			manifestDigest.Algorithm().String(), manifestDigest.Encoded()))
   807  		So(err, ShouldBeNil)
   808  		err = json.Unmarshal(buf, &manifest)
   809  		So(err, ShouldBeNil)
   810  
   811  		manifest.Annotations = make(map[string]string)
   812  
   813  		manifest.Annotations["annotation1"] = "testAnnotation1"
   814  		manifest.Annotations["annotation2"] = "testAnnotation2"
   815  
   816  		// write config
   817  		var imageConfig ispec.Image
   818  		configDigest := manifest.Config.Digest
   819  		buf, err = os.ReadFile(path.Join(dir, "zot-test", "blobs", "sha256",
   820  			configDigest.Encoded()))
   821  		So(err, ShouldBeNil)
   822  		err = json.Unmarshal(buf, &imageConfig)
   823  		So(err, ShouldBeNil)
   824  
   825  		imageConfig.Config.Labels = make(map[string]string)
   826  		imageConfig.Config.Labels["annotation3"] = "testAnnotation3"
   827  
   828  		configContent, err := json.Marshal(imageConfig)
   829  		So(err, ShouldBeNil)
   830  		So(configContent, ShouldNotBeNil)
   831  
   832  		cfgDigest := godigest.FromBytes(configContent)
   833  		So(cfgDigest, ShouldNotBeNil)
   834  
   835  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs", "sha256",
   836  			cfgDigest.Encoded()), configContent, 0o600)
   837  		So(err, ShouldBeNil)
   838  
   839  		// write manifest
   840  		manifest.SchemaVersion = 2
   841  		manifest.Config.Size = int64(len(configContent))
   842  		manifest.Config.Digest = cfgDigest
   843  		manifestContent, err := json.Marshal(manifest)
   844  		So(err, ShouldBeNil)
   845  		So(manifestContent, ShouldNotBeNil)
   846  
   847  		digest := godigest.FromBytes(manifestContent)
   848  		So(digest, ShouldNotBeNil)
   849  
   850  		err = os.WriteFile(path.Join(dir, "zot-test", "blobs",
   851  			digest.Algorithm().String(), digest.Encoded()), manifestContent, 0o600)
   852  		So(err, ShouldBeNil)
   853  
   854  		manifestDesc := ispec.Descriptor{
   855  			Size:   int64(len(manifestContent)),
   856  			Digest: digest,
   857  		}
   858  
   859  		index.Manifests = append(index.Manifests, manifestDesc)
   860  
   861  		linter := lint.NewLinter(lintConfig, log.NewLogger("debug", ""))
   862  		imgStore := local.NewImageStore(dir, false, false,
   863  			log.NewLogger("debug", ""), monitoring.NewMetricsServer(false, log.NewLogger("debug", "")), linter, nil)
   864  
   865  		err = os.Chmod(path.Join(dir, "zot-test", "blobs", "sha256", manifest.Config.Digest.Encoded()), 0o000)
   866  		if err != nil {
   867  			panic(err)
   868  		}
   869  
   870  		pass, err := linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   871  		So(err, ShouldNotBeNil)
   872  		So(pass, ShouldBeFalse)
   873  
   874  		err = os.Chmod(path.Join(dir, "zot-test", "blobs", "sha256", manifest.Config.Digest.Encoded()), 0o755)
   875  		if err != nil {
   876  			panic(err)
   877  		}
   878  
   879  		pass, err = linter.CheckMandatoryAnnotations("zot-test", digest, imgStore)
   880  		So(err, ShouldBeNil)
   881  		So(pass, ShouldBeTrue)
   882  	})
   883  }