Repository: deislabs/ratify
Pull request #1265 (2024-01-17 23:01:56)

feat: validate plugin name on CR create
# Description
https://github.com/deislabs/ratify/issues/1089
This PR adds substatus property to  Verifier and Store CR. The controllers are also updated to validate plugin path.

## What this PR does / why we need it:
- Adding sub status to Verifier and Store CRD
- Adding plugin path check on plugin creation
- Fix a bug where Policy CR isn't writing the success status

## Which issue(s) this PR fixes *(optional, using `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when the PR gets merged)*:
Fixes # 1089

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ X] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Helm Chart Change (any edit/addition/update that is necessary for changes merged to the `main` branch)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Added unit test and e2e test for writing success and failure status in verifier and store

# Checklist:

- [ ] Does the affected code have corresponding tests?
- [ ] Are the changes documented, not just with inline documentation, but also with conceptual documentation such as an overview of a new feature, or task-based documentation like a tutorial? Consider if this change should be announced on your project blog.
- [ ] Does this introduce breaking changes that would require an announcement or bumping the major version?
- [ ] Do all new files have appropriate license header?

# Post Merge Requirements
- [ ] MAINTAINERS: manually trigger the "Publish Package" workflow after merging any PR that indicates `Helm Chart Change`


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) Report
Attention: `13 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`4c61abd`)](https://app.codecov.io/gh/deislabs/ratify/commit/4c61abd77457eab8fe84d40a1000c34384daa6df?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) 63.14% compared to head [(`7f26d68`)](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) 62.98%.

| [Files](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) | Patch % | Lines |
|---|---|---|
| [pkg/controllers/store\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL3N0b3JlX2NvbnRyb2xsZXIuZ28=) | 80.00% | [3 Missing :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [pkg/controllers/verifier\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL3ZlcmlmaWVyX2NvbnRyb2xsZXIuZ28=) | 80.00% | [3 Missing :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [pkg/referrerstore/factory/factory.go](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL3JlZmVycmVyc3RvcmUvZmFjdG9yeS9mYWN0b3J5Lmdv) | 25.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [pkg/verifier/factory/factory.go](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL3ZlcmlmaWVyL2ZhY3RvcnkvZmFjdG9yeS5nbw==) | 0.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [pkg/controllers/policy\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL3BvbGljeV9jb250cm9sbGVyLmdv) | 50.00% | [1 Missing :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1265      +/-   ##
==========================================
- Coverage   63.14%   62.98%   -0.16%     
==========================================
  Files          79       79              
  Lines        5147     5187      +40     
==========================================
+ Hits         3250     3267      +17     
- Misses       1637     1656      +19     
- Partials      260      264       +4     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/deislabs/ratify/pull/1265?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs).

susanshi: @junczhu , @akashsinghal , @binbin-li , this PR is ready for review when you get a chance, thanks!

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 74a94a2..04f87f5 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -46,7 +46,8 @@
       "program": "${workspaceFolder}/cmd/ratify",
       "env": {
         "RATIFY_LOG_LEVEL": "debug",
-        "RATIFY_EXPERIMENTAL_DYNAMIC_PLUGINS": "1"
+        "RATIFY_EXPERIMENTAL_DYNAMIC_PLUGINS": "1",
+        "RATIFY_NAMESPACE": "gatekeeper-system",
       },
       "args": [
         "serve",
diff --git a/Makefile b/Makefile
index 3362a14..a217d7e 100644
--- a/Makefile
+++ b/Makefile
@@ -82,6 +82,7 @@ build-plugins:
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/cosign/... -o ./bin/plugins/ ./plugins/verifier/cosign
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/licensechecker/... -o ./bin/plugins/ ./plugins/verifier/licensechecker
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/sample/... -o ./bin/plugins/ ./plugins/verifier/sample
+	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/referrerstore/sample/... -o ./bin/plugins/referrerstore/ ./plugins/referrerstore/sample
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/sbom/... -o ./bin/plugins/ ./plugins/verifier/sbom
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/schemavalidator/... -o ./bin/plugins/ ./plugins/verifier/schemavalidator
 	go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/vulnerabilityreport/... -o ./bin/plugins/ ./plugins/verifier/vulnerabilityreport
diff --git a/api/v1beta1/store_types.go b/api/v1beta1/store_types.go
index d47eadf..3442278 100644
--- a/api/v1beta1/store_types.go
+++ b/api/v1beta1/store_types.go
@@ -41,12 +41,24 @@ type StoreSpec struct {
 
 // StoreStatus defines the observed state of Store
 type StoreStatus struct {
-	// Important: Run "make" to regenerate code after modifying this file
+	// Important: Run "make install-crds" to regenerate code after modifying this file
+
+	// Is successful in finding the plugin
+	IsSuccess bool `json:"issuccess"`
+	// Error message if operation was unsuccessful
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
 }
 
 // +kubebuilder:object:root=true
 // +kubebuilder:resource:scope="Cluster"
+// +kubebuilder:subresource:status
 // +kubebuilder:storageversion
+// +kubebuilder:printcolumn:name="IsSuccess",type=boolean,JSONPath=`.status.issuccess`
+// +kubebuilder:printcolumn:name="Error",type=string,JSONPath=`.status.brieferror`
 // Store is the Schema for the stores API
 type Store struct {
 	metav1.TypeMeta   `json:",inline"`
diff --git a/api/v1beta1/verifier_types.go b/api/v1beta1/verifier_types.go
index 6f3c6e1..5d4bf09 100644
--- a/api/v1beta1/verifier_types.go
+++ b/api/v1beta1/verifier_types.go
@@ -48,12 +48,24 @@ type VerifierSpec struct {
 // VerifierStatus defines the observed state of Verifier
 type VerifierStatus struct {
 	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
-	// Important: Run "make" to regenerate code after modifying this file
+	// Important: Run "make install-crds" to regenerate code after modifying this file
+
+	// Is successful in finding the plugin
+	IsSuccess bool `json:"issuccess"`
+	// Error message if operation was unsuccessful
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
 }
 
 // +kubebuilder:object:root=true
 // +kubebuilder:resource:scope="Cluster"
+// +kubebuilder:subresource:status
 // +kubebuilder:storageversion
+// +kubebuilder:printcolumn:name="IsSuccess",type=boolean,JSONPath=`.status.issuccess`
+// +kubebuilder:printcolumn:name="Error",type=string,JSONPath=`.status.brieferror`
 // Verifier is the Schema for the verifiers API
 type Verifier struct {
 	metav1.TypeMeta   `json:",inline"`
diff --git a/charts/ratify/crds/store-customresourcedefinition.yaml b/charts/ratify/crds/store-customresourcedefinition.yaml
index 8827fa0..46aa5a8 100644
--- a/charts/ratify/crds/store-customresourcedefinition.yaml
+++ b/charts/ratify/crds/store-customresourcedefinition.yaml
@@ -66,7 +66,14 @@ spec:
         type: object
     served: true
     storage: false
-  - name: v1beta1
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    name: v1beta1
     schema:
       openAPIV3Schema:
         description: Store is the Schema for the stores API
@@ -94,7 +101,7 @@ spec:
                 type: string
               version:
                 description: Version of the store plugin. Optional
-                type: string
+                type: string     
               parameters:
                 description: Parameters of the store
                 type: object
@@ -110,13 +117,27 @@ spec:
                       source, optional
                     type: object
                     x-kubernetes-preserve-unknown-fields: true
-                type: object              
+                type: object
             required:
             - name
             type: object
           status:
             description: StoreStatus defines the observed state of Store
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in finding the plugin
+                type: boolean
+            required:
+            - issuccess
             type: object
         type: object
     served: true
     storage: true
+    subresources:
+      status: {}
diff --git a/charts/ratify/crds/verifier-customresourcedefinition.yaml b/charts/ratify/crds/verifier-customresourcedefinition.yaml
index ccaaddc..0d242ae 100644
--- a/charts/ratify/crds/verifier-customresourcedefinition.yaml
+++ b/charts/ratify/crds/verifier-customresourcedefinition.yaml
@@ -69,7 +69,14 @@ spec:
         type: object
     served: true
     storage: false
-  - name: v1beta1
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    name: v1beta1
     schema:
       openAPIV3Schema:
         description: Verifier is the Schema for the verifiers API
@@ -100,7 +107,7 @@ spec:
                 type: string
               version:
                 description: Version of the verifier plugin. Optional
-                type: string
+                type: string   
               parameters:
                 description: Parameters for this verifier
                 type: object
@@ -123,7 +130,21 @@ spec:
             type: object
           status:
             description: VerifierStatus defines the observed state of Verifier
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in finding the plugin
+                type: boolean
+            required:
+            - issuccess
             type: object
         type: object
     served: true
     storage: true
+    subresources:
+      status: {}
diff --git a/config/crd/bases/config.ratify.deislabs.io_stores.yaml b/config/crd/bases/config.ratify.deislabs.io_stores.yaml
index 409dc7d..0f34fe6 100644
--- a/config/crd/bases/config.ratify.deislabs.io_stores.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_stores.yaml
@@ -67,7 +67,14 @@ spec:
         type: object
     served: true
     storage: false
-  - name: v1beta1
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    name: v1beta1
     schema:
       openAPIV3Schema:
         description: Store is the Schema for the stores API
@@ -95,7 +102,7 @@ spec:
                 type: string
               version:
                 description: Version of the store plugin. Optional
-                type: string                
+                type: string     
               parameters:
                 description: Parameters of the store
                 type: object
@@ -117,7 +124,21 @@ spec:
             type: object
           status:
             description: StoreStatus defines the observed state of Store
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in finding the plugin
+                type: boolean
+            required:
+            - issuccess
             type: object
         type: object
     served: true
     storage: true
+    subresources:
+      status: {}
diff --git a/config/crd/bases/config.ratify.deislabs.io_verifiers.yaml b/config/crd/bases/config.ratify.deislabs.io_verifiers.yaml
index 36584a9..879e6fd 100644
--- a/config/crd/bases/config.ratify.deislabs.io_verifiers.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_verifiers.yaml
@@ -70,7 +70,14 @@ spec:
         type: object
     served: true
     storage: false
-  - name: v1beta1
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    name: v1beta1
     schema:
       openAPIV3Schema:
         description: Verifier is the Schema for the verifiers API
@@ -101,7 +108,7 @@ spec:
                 type: string
               version:
                 description: Version of the verifier plugin. Optional
-                type: string                
+                type: string   
               parameters:
                 description: Parameters for this verifier
                 type: object
@@ -124,7 +131,21 @@ spec:
             type: object
           status:
             description: VerifierStatus defines the observed state of Verifier
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in finding the plugin
+                type: boolean
+            required:
+            - issuccess
             type: object
         type: object
     served: true
     storage: true
+    subresources:
+      status: {}
diff --git a/config/samples/config_v1beta1_store_dynamic.yaml b/config/samples/config_v1beta1_store_dynamic.yaml
new file mode 100644
index 0000000..f916ce8
--- /dev/null
+++ b/config/samples/config_v1beta1_store_dynamic.yaml
@@ -0,0 +1,8 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: Store
+metadata:
+  name: store-dynamic
+spec:
+  name: dynamic  
+  source:
+    artifact: wabbitnetworks.azurecr.io/test/sample-store-plugin:v1
diff --git a/pkg/controllers/policy_controller.go b/pkg/controllers/policy_controller.go
index 109071c..5f29a55 100644
--- a/pkg/controllers/policy_controller.go
+++ b/pkg/controllers/policy_controller.go
@@ -88,6 +88,7 @@ func (r *PolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
 		return ctrl.Result{}, err
 	}
 
+	writePolicyStatus(ctx, r, &policy, policyLogger, true, "")
 	return ctrl.Result{}, nil
 }
 
@@ -159,13 +160,14 @@ func writePolicyStatus(ctx context.Context, r client.StatusClient, policy *confi
 		updatePolicyErrorStatus(policy, errString)
 	}
 	if statusErr := r.Status().Update(ctx, policy); statusErr != nil {
-		logger.Error(statusErr, ", unbale to update policy error status")
+		logger.Error(statusErr, ", unable to update policy error status")
 	}
 }
 
 func updatePolicySuccessStatus(policy *configv1beta1.Policy) {
 	policy.Status.IsSuccess = true
 	policy.Status.Error = ""
+	policy.Status.BriefError = ""
 }
 
 func updatePolicyErrorStatus(policy *configv1beta1.Policy, errString string) {
diff --git a/pkg/controllers/store_controller.go b/pkg/controllers/store_controller.go
index 5f0b1e1..b4421a6 100644
--- a/pkg/controllers/store_controller.go
+++ b/pkg/controllers/store_controller.go
@@ -74,9 +74,12 @@ func (r *StoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
 
 	if err := storeAddOrReplace(store.Spec, resource); err != nil {
 		storeLogger.Error(err, "unable to create store from store crd")
+		writeStoreStatus(ctx, r, &store, storeLogger, false, err.Error())
 		return ctrl.Result{}, err
 	}
 
+	writeStoreStatus(ctx, r, &store, storeLogger, true, "")
+
 	// returning empty result and no error to indicate we’ve successfully reconciled this object
 	return ctrl.Result{}, nil
 }
@@ -140,3 +143,21 @@ func specToStoreConfig(storeSpec configv1beta1.StoreSpec) (rc.StorePluginConfig,
 
 	return storeConfig, nil
 }
+
+func writeStoreStatus(ctx context.Context, r client.StatusClient, store *configv1beta1.Store, logger *logrus.Entry, isSuccess bool, errorString string) {
+	if isSuccess {
+		store.Status.IsSuccess = true
+		store.Status.Error = ""
+		store.Status.BriefError = ""
+	} else {
+		store.Status.IsSuccess = false
+		store.Status.Error = errorString
+		if len(errorString) > maxBriefErrLength {
+			store.Status.BriefError = fmt.Sprintf("%s...", errorString[:maxBriefErrLength])
+		}
+	}
+
+	if statusErr := r.Status().Update(ctx, store); statusErr != nil {
+		logger.Error(statusErr, ",unable to update store error status")
+	}
+}
diff --git a/pkg/controllers/verifier_controller.go b/pkg/controllers/verifier_controller.go
index ee59fb6..31cb83a 100644
--- a/pkg/controllers/verifier_controller.go
+++ b/pkg/controllers/verifier_controller.go
@@ -88,9 +88,12 @@ func (r *VerifierReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
 
 	if err = verifierAddOrReplace(verifier.Spec, resource, namespace); err != nil {
 		verifierLogger.Error(err, "unable to create verifier from verifier crd")
+		writeVerifierStatus(ctx, r, &verifier, verifierLogger, false, err.Error())
 		return ctrl.Result{}, err
 	}
 
+	writeVerifierStatus(ctx, r, &verifier, verifierLogger, true, "")
+
 	// returning empty result and no error to indicate we’ve successfully reconciled this object
 	return ctrl.Result{}, nil
 }
@@ -173,3 +176,21 @@ func getCertStoreNamespace(verifierNamespace string) (string, error) {
 
 	return ns, nil
 }
+
+func writeVerifierStatus(ctx context.Context, r client.StatusClient, verifier *configv1beta1.Verifier, logger *logrus.Entry, isSuccess bool, errorString string) {
+	if isSuccess {
+		verifier.Status.IsSuccess = true
+		verifier.Status.Error = ""
+		verifier.Status.BriefError = ""
+	} else {
+		verifier.Status.IsSuccess = false
+		verifier.Status.Error = errorString
+		if len(errorString) > maxBriefErrLength {
+			verifier.Status.BriefError = fmt.Sprintf("%s...", errorString[:maxBriefErrLength])
+		}
+	}
+
+	if statusErr := r.Status().Update(ctx, verifier); statusErr != nil {
+		logger.Error(statusErr, ",unable to update verifier status")
+	}
+}
diff --git a/pkg/referrerstore/factory/factory.go b/pkg/referrerstore/factory/factory.go
index 099632a..fb80faa 100644
--- a/pkg/referrerstore/factory/factory.go
+++ b/pkg/referrerstore/factory/factory.go
@@ -50,6 +50,7 @@ func Register(name string, factory StoreFactory) {
 	builtInStores[name] = factory
 }
 
+// the first element of pluginBinDir will be used as the plugin directory
 func CreateStoreFromConfig(storeConfig config.StorePluginConfig, configVersion string, pluginBinDir []string) (referrerstore.ReferrerStore, error) {
 	storeName, ok := storeConfig[types.Name]
 	if !ok {
@@ -84,6 +85,11 @@ func CreateStoreFromConfig(storeConfig config.StorePluginConfig, configVersion s
 	if ok {
 		return storeFactory.Create(configVersion, storeConfig)
 	}
+
+	if _, err := pluginCommon.FindInPaths(storeNameStr, pluginBinDir); err != nil {
+		return nil, re.ErrorCodePluginNotFound.NewError(re.ReferrerStore, "", re.EmptyLink, err, "plugin not found", re.HideStackTrace)
+	}
+
 	return plugin.NewStore(configVersion, storeConfig, pluginBinDir)
 }
 
diff --git a/pkg/verifier/factory/factory.go b/pkg/verifier/factory/factory.go
index 19e1403..43e16be 100644
--- a/pkg/verifier/factory/factory.go
+++ b/pkg/verifier/factory/factory.go
@@ -51,6 +51,7 @@ func Register(name string, factory VerifierFactory) {
 
 // returns a single verifier from a verifierConfig
 // namespace is only applicable in K8s environment, namespace is appended to the certstore of the truststore so it is uniquely identifiable in a cluster env
+// the first element of pluginBinDir will be used as the plugin directory
 func CreateVerifierFromConfig(verifierConfig config.VerifierConfig, configVersion string, pluginBinDir []string, namespace string) (verifier.ReferenceVerifier, error) {
 	// in cli mode both `type` and `name`` are read from config, if `type` is not specified, `name` is used as `type`
 	var verifierTypeStr string
@@ -91,6 +92,11 @@ func CreateVerifierFromConfig(verifierConfig config.VerifierConfig, configVersio
 	if ok {
 		return verifierFactory.Create(configVersion, verifierConfig, pluginBinDir[0], namespace)
 	}
+
+	if _, err := pluginCommon.FindInPaths(verifierTypeStr, pluginBinDir); err != nil {
+		return nil, re.ErrorCodePluginNotFound.NewError(re.Verifier, "", re.EmptyLink, err, "plugin not found", re.HideStackTrace)
+	}
+
 	return plugin.NewVerifier(configVersion, verifierConfig, pluginBinDir)
 }
 


Test patch:
diff --git a/pkg/controllers/store_controller_test.go b/pkg/controllers/store_controller_test.go
index 167744b..75196d8 100644
--- a/pkg/controllers/store_controller_test.go
+++ b/pkg/controllers/store_controller_test.go
@@ -16,17 +16,24 @@ limitations under the License.
 package controllers
 
 import (
+	"context"
+	"os"
+	"path/filepath"
+	"strings"
 	"testing"
 
 	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
 	"github.com/deislabs/ratify/pkg/referrerstore"
+	"github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 func TestStoreAdd_EmptyParameter(t *testing.T) {
 	resetStoreMap()
 	var testStoreSpec = configv1beta1.StoreSpec{
-		Name: "oras",
+		Name:    "sample",
+		Address: getVerifierPluginsDir(),
 	}
 
 	if err := storeAddOrReplace(testStoreSpec, "oras"); err != nil {
@@ -53,11 +60,62 @@ func TestStoreAdd_WithParameters(t *testing.T) {
 	}
 }
 
+func TestWriteStoreStatus(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		store      *configv1beta1.Store
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			store:      &configv1beta1.Store{},
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			store:      &configv1beta1.Store{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:      "status update failed",
+			isSuccess: true,
+			store:     &configv1beta1.Store{},
+			reconciler: &mockStatusClient{
+				updateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeStoreStatus(context.Background(), tc.reconciler, tc.store, logger, tc.isSuccess, tc.errString)
+		})
+	}
+}
+
+func TestStoreAddOrReplace_PluginNotFound(t *testing.T) {
+	resetStoreMap()
+	var resource = "invalidplugin"
+	expectedMsg := "plugin not found"
+	var spec = getInvalidStoreSpec()
+	err := storeAddOrReplace(spec, resource)
+
+	if !strings.Contains(err.Error(), expectedMsg) {
+		t.Fatalf("TestStoreAddOrReplace_PluginNotFound expected msg: '%v', actual %v", expectedMsg, err.Error())
+	}
+}
+
 func TestStore_UpdateAndDelete(t *testing.T) {
 	resetStoreMap()
 	// add a Store
 
-	var resource = "oras"
+	var resource = "sample"
 
 	var testStoreSpec = getOrasStoreSpec()
 
@@ -70,7 +128,8 @@ func TestStore_UpdateAndDelete(t *testing.T) {
 
 	// modify the Store
 	var updatedSpec = configv1beta1.StoreSpec{
-		Name: "oras",
+		Name:    "sample",
+		Address: getStorePluginsDir(),
 	}
 
 	if err := storeAddOrReplace(updatedSpec, resource); err != nil {
@@ -98,9 +157,23 @@ func getOrasStoreSpec() configv1beta1.StoreSpec {
 	var storeParameters = []byte(parametersString)
 
 	return configv1beta1.StoreSpec{
-		Name: "oras",
+		Name:    "sample",
+		Address: getStorePluginsDir(),
 		Parameters: runtime.RawExtension{
 			Raw: storeParameters,
 		},
 	}
 }
+
+func getStorePluginsDir() string {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../..", "./bin/plugins/referrerstore/"))
+	return pluginDir
+}
+
+func getInvalidStoreSpec() configv1beta1.StoreSpec {
+	return configv1beta1.StoreSpec{
+		Name:    "pluginnotfound",
+		Address: getStorePluginsDir(),
+	}
+}
diff --git a/pkg/controllers/verifier_controller_test.go b/pkg/controllers/verifier_controller_test.go
index ca4b7f1..1ab5901 100644
--- a/pkg/controllers/verifier_controller_test.go
+++ b/pkg/controllers/verifier_controller_test.go
@@ -16,14 +16,19 @@ limitations under the License.
 package controllers
 
 import (
+	"context"
 	"os"
+	"path/filepath"
+	"strings"
 	"testing"
 
 	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
 	"github.com/deislabs/ratify/internal/constants"
 	"github.com/deislabs/ratify/pkg/utils"
 	vr "github.com/deislabs/ratify/pkg/verifier"
+	"github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 func TestMain(m *testing.M) {
@@ -36,10 +41,11 @@ func TestMain(m *testing.M) {
 func TestVerifierAdd_EmptyParameter(t *testing.T) {
 	resetVerifierMap()
 	var testVerifierSpec = configv1beta1.VerifierSpec{
-		Name:          "notation",
+		Name:          "sample",
 		ArtifactTypes: "application/vnd.cncf.notary.signature",
+		Address:       getVerifierPluginsDir(),
 	}
-	var resource = "notation"
+	var resource = "sample"
 
 	if err := verifierAddOrReplace(testVerifierSpec, resource, constants.EmptyNamespace); err != nil {
 		t.Fatalf("verifierAddOrReplace() expected no error, actual %v", err)
@@ -65,6 +71,18 @@ func TestVerifierAdd_WithParameters(t *testing.T) {
 	}
 }
 
+func TestVerifierAddOrReplace_PluginNotFound(t *testing.T) {
+	resetVerifierMap()
+	var resource = "invalidplugin"
+	expectedMsg := "plugin not found"
+	var testVerifierSpec = getInvalidVerifierSpec()
+	err := verifierAddOrReplace(testVerifierSpec, resource, constants.EmptyNamespace)
+
+	if !strings.Contains(err.Error(), expectedMsg) {
+		t.Fatalf("TestVerifierAddOrReplace_PluginNotFound expected msg: '%v', actual %v", expectedMsg, err.Error())
+	}
+}
+
 func TestVerifier_UpdateAndDelete(t *testing.T) {
 	resetVerifierMap()
 
@@ -98,6 +116,54 @@ func TestVerifier_UpdateAndDelete(t *testing.T) {
 	}
 }
 
+func TestWriteVerifierStatus(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		verifier   *configv1beta1.Verifier
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			errString:  "",
+			verifier:   &configv1beta1.Verifier{},
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			verifier:   &configv1beta1.Verifier{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:      "status update failed",
+			isSuccess: true,
+			verifier:  &configv1beta1.Verifier{},
+			reconciler: &mockStatusClient{
+				updateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeVerifierStatus(context.Background(), tc.reconciler, tc.verifier, logger, tc.isSuccess, tc.errString)
+
+			if tc.verifier.Status.IsSuccess != tc.isSuccess {
+				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.verifier.Status.IsSuccess)
+			}
+
+			if tc.verifier.Status.Error != tc.errString {
+				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.verifier.Status.Error)
+			}
+		})
+	}
+}
+
 func TestGetCertStoreNamespace(t *testing.T) {
 	// error scenario, everything is empty, expect error
 	_, err := getCertStoreNamespace("")
@@ -133,13 +199,28 @@ func getLicenseCheckerFromParam(parametersString string) configv1beta1.VerifierS
 	return configv1beta1.VerifierSpec{
 		Name:          "licensechecker",
 		ArtifactTypes: "application/vnd.ratify.spdx.v0",
+		Address:       getVerifierPluginsDir(),
 		Parameters: runtime.RawExtension{
 			Raw: allowedLicenses,
 		},
 	}
 }
 
+func getInvalidVerifierSpec() configv1beta1.VerifierSpec {
+	return configv1beta1.VerifierSpec{
+		Name:          "pluginnotfound",
+		ArtifactTypes: "application/vnd.ratify.spdx.v0",
+		Address:       getVerifierPluginsDir(),
+	}
+}
+
 func getDefaultLicenseCheckerSpec() configv1beta1.VerifierSpec {
 	var parametersString = "{\"allowedLicenses\":[\"MIT\",\"Apache\"]}"
 	return getLicenseCheckerFromParam(parametersString)
 }
+
+func getVerifierPluginsDir() string {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../..", "./bin/plugins"))
+	return pluginDir
+}
diff --git a/pkg/referrerstore/factory/factory_test.go b/pkg/referrerstore/factory/factory_test.go
index 9e46d32..babd4b9 100644
--- a/pkg/referrerstore/factory/factory_test.go
+++ b/pkg/referrerstore/factory/factory_test.go
@@ -19,6 +19,7 @@ import (
 	"errors"
 	"os"
 	"path"
+	"path/filepath"
 	"testing"
 
 	"github.com/deislabs/ratify/pkg/featureflag"
@@ -46,7 +47,7 @@ func TestCreateStoresFromConfig_BuiltInStores_ReturnsExpected(t *testing.T) {
 		Stores: []config.StorePluginConfig{storeConfig},
 	}
 
-	stores, err := CreateStoresFromConfig(storesConfig, "")
+	stores, err := CreateStoresFromConfig(storesConfig, getReferrerstorePluginsDir())
 
 	if err != nil {
 		t.Fatalf("create stores failed with err %v", err)
@@ -67,13 +68,13 @@ func TestCreateStoresFromConfig_BuiltInStores_ReturnsExpected(t *testing.T) {
 
 func TestCreateStoresFromConfig_PluginStores_ReturnsExpected(t *testing.T) {
 	storeConfig := map[string]interface{}{
-		"name": "plugin-store",
+		"name": "sample",
 	}
 	storesConfig := config.StoresConfig{
 		Stores: []config.StorePluginConfig{storeConfig},
 	}
 
-	stores, err := CreateStoresFromConfig(storesConfig, "")
+	stores, err := CreateStoresFromConfig(storesConfig, getReferrerstorePluginsDir())
 
 	if err != nil {
 		t.Fatalf("create stores failed with err %v", err)
@@ -83,7 +84,7 @@ func TestCreateStoresFromConfig_PluginStores_ReturnsExpected(t *testing.T) {
 		t.Fatalf("expected to have %d stores, actual count %d", 1, len(stores))
 	}
 
-	if stores[0].Name() != "plugin-store" {
+	if stores[0].Name() != "sample" {
 		t.Fatalf("expected to create plugin store")
 	}
 
@@ -95,6 +96,7 @@ func TestCreateStoresFromConfig_PluginStores_ReturnsExpected(t *testing.T) {
 func TestCreateStoresFromConfig_DynamicPluginStores_ReturnsExpected(t *testing.T) {
 	os.Setenv("RATIFY_EXPERIMENTAL_DYNAMIC_PLUGINS", "1")
 	featureflag.InitFeatureFlagsFromEnv()
+
 	testCases := []struct {
 		name     string
 		artifact string
@@ -121,8 +123,7 @@ func TestCreateStoresFromConfig_DynamicPluginStores_ReturnsExpected(t *testing.T
 			storesConfig := config.StoresConfig{
 				Stores: []config.StorePluginConfig{storeConfig},
 			}
-
-			stores, err := CreateStoresFromConfig(storesConfig, "")
+			stores, err := CreateStoresFromConfig(storesConfig, getReferrerstorePluginsDir())
 
 			if err != nil {
 				t.Fatalf("create stores failed with err %v", err)
@@ -147,3 +148,9 @@ func TestCreateStoresFromConfig_DynamicPluginStores_ReturnsExpected(t *testing.T
 		})
 	}
 }
+
+func getReferrerstorePluginsDir() string {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../../../", "./bin/plugins/referrerstore/"))
+	return pluginDir
+}
diff --git a/pkg/referrerstore/plugin/skel/skel_test.go b/pkg/referrerstore/plugin/skel/skel_test.go
index 1f58993..9022637 100644
--- a/pkg/referrerstore/plugin/skel/skel_test.go
+++ b/pkg/referrerstore/plugin/skel/skel_test.go
@@ -18,6 +18,8 @@ package skel
 import (
 	"bytes"
 	"fmt"
+	"os"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -30,8 +32,8 @@ import (
 	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
-const (
-	testStdinData = `{ "name":"skel-test-case", "some": "config" }`
+var (
+	testStdinData = fmt.Sprintf(`{ "name":"skel-test-case", "some": "config","pluginBinDirs": ["%s"]}`, getPluginsDir())
 )
 
 func TestPluginMain_GetBlobContent_ReturnsExpected(t *testing.T) {
@@ -220,7 +222,7 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", nil, getBlobContent, nil, nil, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrConfigParsingFailure {
-		t.Fatalf("plugin execution expected to fail with error code %d for invalid config", types.ErrConfigParsingFailure)
+		t.Fatalf("plugin execution expected to fail with error code %d for invalid config, actual error: %s", types.ErrConfigParsingFailure, err)
 	}
 
 	stdinData = ` {"some": "config" }`
@@ -316,3 +318,9 @@ func TestPluginMain_ListReferrers_ErrorCases(t *testing.T) {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid arg", types.ErrArgsParsingFailure)
 	}
 }
+
+func getPluginsDir() string {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../../../../", "./bin/plugins/referrerstore/"))
+	return pluginDir
+}
diff --git a/pkg/verifier/factory/factory_test.go b/pkg/verifier/factory/factory_test.go
index 9632f3e..54a865b 100644
--- a/pkg/verifier/factory/factory_test.go
+++ b/pkg/verifier/factory/factory_test.go
@@ -17,6 +17,8 @@ package factory
 
 import (
 	"context"
+	"os"
+	"path/filepath"
 	"testing"
 
 	"github.com/deislabs/ratify/internal/constants"
@@ -102,15 +104,18 @@ func TestCreateVerifiersFromConfig_BuiltInVerifiers_ReturnsExpected(t *testing.T
 }
 
 func TestCreateVerifiersFromConfig_PluginVerifiers_ReturnsExpected(t *testing.T) {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../../..", "./bin/plugins"))
+
 	verifierConfig := map[string]interface{}{
 		"name": "plugin-verifier-0",
-		"type": "plugin-verifier",
+		"type": "sample",
 	}
 	verifiersConfig := config.VerifiersConfig{
 		Verifiers: []config.VerifierConfig{verifierConfig},
 	}
 
-	verifiers, err := CreateVerifiersFromConfig(verifiersConfig, "", "")
+	verifiers, err := CreateVerifiersFromConfig(verifiersConfig, pluginDir, "")
 
 	if err != nil {
 		t.Fatalf("create verifiers failed with err %v", err)
diff --git a/pkg/verifier/plugin/skel/skel_test.go b/pkg/verifier/plugin/skel/skel_test.go
index 9367c40..3e2e4cd 100644
--- a/pkg/verifier/plugin/skel/skel_test.go
+++ b/pkg/verifier/plugin/skel/skel_test.go
@@ -18,6 +18,8 @@ package skel
 import (
 	"bytes"
 	"fmt"
+	"os"
+	"path/filepath"
 	"reflect"
 	"strings"
 	"testing"
@@ -41,12 +43,12 @@ func TestPluginMain_VerifyReference_ReturnsExpected(t *testing.T) {
 			t.Fatalf("expected artifact type %s actual %s", "test-type", referenceDescriptor.ArtifactType)
 		}
 
-		if referrerStore.Name() != "test-store" {
-			t.Fatalf("expected store name %s actual %s", "test-store", referrerStore.Name())
+		if referrerStore.Name() != "sample" {
+			t.Fatalf("expected store name %s actual %s", "sample", 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", ""}
+		expectedPluginBinDirs := []string{getReferrerstorePluginsDir(), ""}
 		pluginStore := referrerStore.(*sp.StorePlugin)
 		actualPluginBinDirs := pluginStore.GetPath()
 		if !reflect.DeepEqual(expectedPluginBinDirs, actualPluginBinDirs) {
@@ -62,7 +64,7 @@ func TestPluginMain_VerifyReference_ReturnsExpected(t *testing.T) {
 		plugin.SubjectEnvKey: "localhost:5000/net-monitor:v1@sha256:a0fc570a245b09ed752c42d600ee3bb5b4f77bbd70d8898780b7ab43454530eb",
 	}
 
-	stdinData := `{ "storeConfig" : {"store": {"name":"test-store", "some": "config"}, "pluginBinDirs": ["/tmp/ratify/plugins"]}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData := fmt.Sprintf(`{ "storeConfig" : {"store": {"name":"sample", "some": "config"}, "pluginBinDirs": ["%s"]}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	stdout := &bytes.Buffer{}
 	stderr := &bytes.Buffer{}
 	pluginContext := &pcontext{
@@ -129,7 +131,7 @@ func TestPluginMain_ErrorCases(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 := fmt.Sprintf(`{ "storeConfig" : {"store": {"name":"sample", "some": "config"}}, "pluginBinDirs": ["%s"], "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	stdout := &bytes.Buffer{}
 	stderr := &bytes.Buffer{}
 	pluginContext := &pcontext{
@@ -161,14 +163,14 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 
 	environment[plugin.VersionEnvKey] = "1.0.0"
 
-	stdinData = `"storeConfig" : {"store": {"name":"test-store", "some": "config"}}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData = fmt.Sprintf(`"storeConfig" : {"store": {"name":"sample", "some": "config"}, "pluginBinDirs": ["%s"]},"config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", verifyReference, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrConfigParsingFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for invalid config", types.ErrConfigParsingFailure)
 	}
 
-	stdinData = `{"storeConfig" : {"store": {"name":"test-store", "some": "config"}}, "config": {"some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData = fmt.Sprintf(`{"storeConfig" : {"store": {"name":"sample", "some": "config"}, "pluginBinDirs": ["%s"]}, "config": {"some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", verifyReference, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrInvalidVerifierConfig {
@@ -176,18 +178,24 @@ func TestPluginMain_ErrorCases(t *testing.T) {
 	}
 
 	environment[plugin.CommandEnvKey] = "unknown"
-	stdinData = `{"storeConfig" : {"store": {"name":"test-store", "some": "config"}}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData = fmt.Sprintf(`{"storeConfig" : {"store": {"name":"sample", "some": "config"}, "pluginBinDirs": ["%s"]},  "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", verifyReference, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrUnknownCommand {
-		t.Fatalf("plugin execution expected to fail with error code %d for invalid command", types.ErrUnknownCommand)
+		t.Fatalf("plugin execution expected to fail with error code %d for invalid command, actual err :%v", types.ErrUnknownCommand, err)
 	}
 
 	environment[plugin.CommandEnvKey] = plugin.VerifyCommand
-	stdinData = `{"storeConfig" : {"store": {"name":"test-store", "some": "config"}}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`
+	stdinData = fmt.Sprintf(`{"storeConfig" : {"store": {"name":"sample", "some": "config"}, "pluginBinDirs": ["%s"]}, "config": {"name": "skel-test-case", "some":"config"}, "referenceDesc": {"artifactType": "test-type"}}`, getReferrerstorePluginsDir())
 	pluginContext.Stdin = strings.NewReader(stdinData)
 	err = pluginContext.pluginMainCore("skel-test-case", "1.0.0", verifyReference, []string{"1.0.0"})
 	if err == nil || err.Code != types.ErrPluginCmdFailure {
 		t.Fatalf("plugin execution expected to fail with error code %d for cmd failure", types.ErrPluginCmdFailure)
 	}
 }
+
+func getReferrerstorePluginsDir() string {
+	workingDir, _ := os.Getwd()
+	pluginDir := filepath.Clean(filepath.Join(workingDir, "../../../../", "./bin/plugins/referrerstore/"))
+	return pluginDir
+}
diff --git a/test/bats/base-test.bats b/test/bats/base-test.bats
index 45fae7c..e9671c3 100644
--- a/test/bats/base-test.bats
+++ b/test/bats/base-test.bats
@@ -166,6 +166,50 @@ RATIFY_NAMESPACE=gatekeeper-system
     assert_success
 }
 
+@test "verifier crd status check" {
+    teardown() {
+        echo "cleaning up"
+        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete verifiers.config.ratify.deislabs.io/verifier-license-checker'
+    }
+
+    # apply a valid verifier, validate status property shows success
+    run kubectl apply -f ./config/samples/config_v1beta1_verifier_complete_licensechecker.yaml
+    assert_success
+    run bash -c "kubectl describe verifiers.config.ratify.deislabs.io/verifier-license-checker -n ${RATIFY_NAMESPACE} | grep 'Issuccess:  true'"
+    assert_success
+
+    # apply a invalid verifier CR, validate status with error
+    sed 's/licensechecker/invalidlicensechecker/' ./config/samples/config_v1beta1_verifier_complete_licensechecker.yaml > invalidVerifier.yaml
+    run kubectl apply -f invalidVerifier.yaml
+    assert_success
+    run bash -c "kubectl describe verifiers.config.ratify.deislabs.io/verifier-license-checker -n ${RATIFY_NAMESPACE} | grep 'Brieferror:  Original Error:'"
+    assert_success
+
+    # apply a valid verifier, validate status property shows success
+    run kubectl apply -f ./config/samples/config_v1beta1_verifier_complete_licensechecker.yaml
+    assert_success
+    run bash -c "kubectl describe verifiers.config.ratify.deislabs.io/verifier-license-checker -n ${RATIFY_NAMESPACE} | grep 'Issuccess:  true'"
+    assert_success
+    run bash -c "kubectl describe verifiers.config.ratify.deislabs.io/verifier-license-checker -n ${RATIFY_NAMESPACE} | grep 'Brieferror:  Original Error:'"
+    assert_failure
+}
+
+@test "store crd status check" {
+    teardown() {
+        echo "cleaning up"
+        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete stores.config.ratify.deislabs.io/store-dynamic'
+    }
+
+    # apply a invalid verifier CR, validate status with error
+    sed 's/:v1/:invalid/' ./config/samples/config_v1beta1_store_dynamic.yaml > invalidstore.yaml
+    run kubectl apply -f invalidstore.yaml
+    assert_success
+    # wait for download of image
+    sleep 5
+    run bash -c "kubectl describe stores.config.ratify.deislabs.io/store-dynamic -n ${RATIFY_NAMESPACE} | grep 'plugin not found'"
+    assert_success
+}
+
 @test "configmap update test" {
     skip "Skipping test for now as we are no longer watching for configfile update in a K8s environment. This test ensures we are watching config file updates in a non-kub scenario"
     run kubectl apply -f ./library/default/template.yaml