Repository: deislabs/ratify (go)

Issue:
Store plugins cannot be used from verifier plugins
When executing verifier plugins, Ratify will bundle up and pass all of the configuration needed about a store to a verifier plugin over STDIN. An important field in this flow is `pluginBinDirs`, as that is how Ratify knows where to find plugins - either in the default plugin dir, or a user-provided dir. 

Today, the verifier plugin skeleton ignores that STDIN input and uses an explicit `nil` and `""` as the `pluginBinDirs` paths for stores. The effect is that when an out of tree store (ex: anything not `oras`) is resolved, it fails at runtime with an error because the store can't be found under a blank subdir. Ex:

```
    {
      "isSuccess": false,
      "name": "sample",
      "message": "an error thrown by the verifier: plugin command VERIFY failed; failed to find plugin \"oras2\" in paths []",
      "artifactType": "application/spdx+json"
    },
```

Per some code comments nearby, it looks like this used to work, but there has been limited usage of plugin stores outside of the sample in the repo.

Pull request:
Enable verifier plugins to work with store plugins
# Description

## What this PR does / why we need it:

- Enables verifier plugins to consume `pluginBinDirs` from STDIN and use them to resolve store plugins
- Adds tests to validate that the path is used
- Adds test coverage to ensure that builtin stores are correctly resolved

Fixes #492

## Type of change

Please delete options that are not relevant.

- [x] Bug fix (non-breaking change which fixes an issue)

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration

- [x] Added/updated unit tests
- [x] Manually built a store plugin and validated against a verifier plugin

# Checklist:

- [x] Does the affected code have corresponding tests?


Patch:
diff --git a/pkg/referrerstore/plugin/plugin.go b/pkg/referrerstore/plugin/plugin.go
index 5137061..83fe03c 100644
--- a/pkg/referrerstore/plugin/plugin.go
+++ b/pkg/referrerstore/plugin/plugin.go
@@ -201,3 +201,7 @@ func (sp *StorePlugin) GetConfig() *config.StoreConfig {
 		Store:         sp.rawConfig,
 	}
 }
+
+func (sp *StorePlugin) GetPath() []string {
+	return sp.path
+}
diff --git a/pkg/verifier/plugin/skel/skel.go b/pkg/verifier/plugin/skel/skel.go
index 9e24a39..2010e26 100644
--- a/pkg/verifier/plugin/skel/skel.go
+++ b/pkg/verifier/plugin/skel/skel.go
@@ -87,7 +87,7 @@ func (pc *pcontext) pluginMainCore(name, version string, verifyReference VerifyR
 	// The below is a workaround to be able to use built-in referrer store plugins from within verifier plugins
 	storeConfigs := storeConfig.StoresConfig{
 		Version:       version,
-		PluginBinDirs: nil,
+		PluginBinDirs: input.StoreConfig.PluginBinDirs,
 		Stores:        []storeConfig.StorePluginConfig{input.StoreConfig.Store},
 	}
 	stores, storeErr := factory.CreateStoresFromConfig(storeConfigs, "")


Test patch:
diff --git a/pkg/verifier/plugin/skel/skel_test.go b/pkg/verifier/plugin/skel/skel_test.go
index 7cd343e..ed2a2ed 100644
--- a/pkg/verifier/plugin/skel/skel_test.go
+++ b/pkg/verifier/plugin/skel/skel_test.go
@@ -18,17 +18,21 @@ package skel
 import (
 	"bytes"
 	"fmt"
+	"reflect"
 	"strings"
 	"testing"
 
 	"github.com/deislabs/ratify/pkg/common"
 	"github.com/deislabs/ratify/pkg/ocispecs"
 	"github.com/deislabs/ratify/pkg/referrerstore"
-
+	sp "github.com/deislabs/ratify/pkg/referrerstore/plugin"
 	"github.com/deislabs/ratify/pkg/verifier/plugin"
 	"github.com/deislabs/ratify/pkg/verifier/types"
 
 	"github.com/deislabs/ratify/pkg/verifier"
+
+	// This import is required to utilize the oras built-in referrer store
+	_ "github.com/deislabs/ratify/pkg/referrerstore/oras"
 )
 
 func TestPluginMain_VerifyReference_ReturnsExpected(t *testing.T) {
@@ -40,6 +44,15 @@ func TestPluginMain_VerifyReference_ReturnsExpected(t *testing.T) {
 		if referrerStore.Name() != "test-store" {
 			t.Fatalf("expected store name %s actual %s", "test-store", referrerStore.Name())
 		}
+
+		// the parsed pluginBinDirs should include the data that was provided by Ratify, plus the default (currently assumed to be "")
+		expectedPluginBinDirs := []string{"/tmp/ratify/plugins", ""}
+		pluginStore := referrerStore.(*sp.StorePlugin)
+		actualPluginBinDirs := pluginStore.GetPath()
+		if !reflect.DeepEqual(expectedPluginBinDirs, actualPluginBinDirs) {
+			t.Fatalf("expected plugin bin dirs %#v actual %#v", expectedPluginBinDirs, actualPluginBinDirs)
+		}
+
 		return &verifier.VerifierResult{IsSuccess: true}, nil
 	}
 
@@ -49,7 +62,44 @@ func TestPluginMain_VerifyReference_ReturnsExpected(t *testing.T) {
 		plugin.SubjectEnvKey: "localhost:5000/net-monitor:v1@sha256:a0fc570a245b09ed752c42d600ee3bb5b4f77bbd70d8898780b7ab43454530eb",
 	}
 
-	stdinData := `{ "storeConfig" : {"store": {"name":"test-store", "some": "config"}}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData := `{ "storeConfig" : {"store": {"name":"test-store", "some": "config"}, "pluginBinDirs": ["/tmp/ratify/plugins"]}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdout := &bytes.Buffer{}
+	stderr := &bytes.Buffer{}
+	pluginContext := &pcontext{
+		GetEnviron: func(key string) string { return environment[key] },
+		Stdin:      strings.NewReader(stdinData),
+		Stdout:     stdout,
+		Stderr:     stderr,
+	}
+
+	err := pluginContext.pluginMainCore("skel-test-case", "1.0.0", verifyReference, []string{"1.0.0"})
+	if err != nil {
+		t.Fatalf("plugin execution failed %v", err)
+	}
+
+	out := fmt.Sprintf("%s", stdout)
+	if !strings.Contains(out, `"isSuccess":true`) {
+		t.Fatalf("plugin execution failed. expected %v actual %v", "isSuccess: true", out)
+	}
+}
+
+func TestPluginMain_VerifyReference_CanUseBuiltinStores(t *testing.T) {
+	verifyReference := func(args *CmdArgs, subjectReference common.Reference, referenceDescriptor ocispecs.ReferenceDescriptor, referrerStore referrerstore.ReferrerStore) (*verifier.VerifierResult, error) {
+		// expect to find a builtin store and fail if it was configured as a plugin
+		if _, ok := referrerStore.(*sp.StorePlugin); ok {
+			t.Fatalf("expected store to be builtin")
+		}
+
+		return &verifier.VerifierResult{IsSuccess: true}, nil
+	}
+
+	environment := map[string]string{
+		plugin.CommandEnvKey: plugin.VerifyCommand,
+		plugin.VersionEnvKey: "1.0.0",
+		plugin.SubjectEnvKey: "localhost:5000/net-monitor:v1@sha256:a0fc570a245b09ed752c42d600ee3bb5b4f77bbd70d8898780b7ab43454530eb",
+	}
+
+	stdinData := `{ "storeConfig" : {"store": {"name":"oras"}}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
 	stdout := &bytes.Buffer{}
 	stderr := &bytes.Buffer{}
 	pluginContext := &pcontext{