Repository: aquasecurity/starboard
Pull request #593 (2021-05-21 18:51:42)

fix: Flaky CLI Integration Test
Resolves https://github.com/aquasecurity/starboard/issues/561

codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) Report
> Merging [#593](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (459ca15) into [main](https://codecov.io/gh/aquasecurity/starboard/commit/f500bd4c93aa6ae2e5c460970229339e319e3f7f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (f500bd4) will **decrease** coverage by `25.17%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/593/graphs/tree.svg?width=650&height=150&src=pr&token=qqTGKAJ0Wb&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)

```diff
@@             Coverage Diff             @@
##             main     #593       +/-   ##
===========================================
- Coverage   71.17%   45.99%   -25.18%     
===========================================
  Files          58       42       -16     
  Lines        4152     3507      -645     
===========================================
- Hits         2955     1613     -1342     
- Misses        871     1799      +928     
+ Partials      326       95      -231     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | Coverage Δ | |
|---|---|---|
| [pkg/kubehunter/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmVodW50ZXIvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-82.41%)` | :arrow_down: |
| [pkg/plugin/factory.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3BsdWdpbi9mYWN0b3J5Lmdv) | `0.00% <0.00%> (-77.15%)` | :arrow_down: |
| [pkg/vulnerabilityreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL3Z1bG5lcmFiaWxpdHlyZXBvcnQvc2Nhbm5lci5nbw==) | `0.00% <0.00%> (-73.53%)` | :arrow_down: |
| [pkg/configauditreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2NvbmZpZ2F1ZGl0cmVwb3J0L3NjYW5uZXIuZ28=) | `0.00% <0.00%> (-73.22%)` | :arrow_down: |
| [pkg/operator/controller/vulnerabilityreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvdnVsbmVyYWJpbGl0eXJlcG9ydC5nbw==) | `0.00% <0.00%> (-69.24%)` | :arrow_down: |
| [pkg/operator/controller/configauditreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY29uZmlnYXVkaXRyZXBvcnQuZ28=) | `0.00% <0.00%> (-65.39%)` | :arrow_down: |
| [pkg/kube/runnable\_job.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmUvcnVubmFibGVfam9iLmdv) | `0.00% <0.00%> (-64.39%)` | :arrow_down: |
| [pkg/operator/controller/ciskubebenchreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvY2lza3ViZWJlbmNocmVwb3J0Lmdv) | `0.00% <0.00%> (-62.23%)` | :arrow_down: |
| [pkg/kubehunter/model.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL2t1YmVodW50ZXIvbW9kZWwuZ28=) | `0.00% <0.00%> (-61.54%)` | :arrow_down: |
| [pkg/operator/controller/plugins\_config.go](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2NvbnRyb2xsZXIvcGx1Z2luc19jb25maWcuZ28=) | `0.00% <0.00%> (-51.36%)` | :arrow_down: |
| ... and [29 more](https://codecov.io/gh/aquasecurity/starboard/pull/593/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Last update [b68e311...459ca15](https://codecov.io/gh/aquasecurity/starboard/pull/593?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity).


Patch:
diff --git a/deploy/crd/vulnerabilityreports.crd.yaml b/deploy/crd/vulnerabilityreports.crd.yaml
index a293ce0..8358eae 100644
--- a/deploy/crd/vulnerabilityreports.crd.yaml
+++ b/deploy/crd/vulnerabilityreports.crd.yaml
@@ -31,11 +31,15 @@ spec:
             report:
               type: object
               required:
+                - updateTimestamp
                 - scanner
                 - artifact
                 - summary
                 - vulnerabilities
               properties:
+                updateTimestamp:
+                  type: string
+                  format: date-time
                 scanner:
                   type: object
                   required:
diff --git a/docs/settings.md b/docs/settings.md
index e1f972c..a6b0eab 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -81,7 +81,8 @@ The following tables list available configuration settings with their default va
 | `polaris.imageRef`              | `quay.io/fairwinds/polaris:3.2`                        | Polaris image reference |
 | `polaris.config.yaml`           | [Check the default value here][default-polaris-config] | Polaris configuration file |
 | `conftest.imageRef`             | `docker.io/openpolicyagent/conftest:v0.25.0`           | Conftest image reference |
-| `scanJob.tolerations`           | N/A                                                    | JSON representation of the [tolerations] to be applied to the vulnerability scanner pods so that they can run on nodes with matching taints. Example: `'[{"key":"key1", "operator":"Equal", "value":"value1", "effect":"NoSchedule"}]'`|
+| `scanJob.tolerations`           | N/A                                                    | JSON representation of the [tolerations] to be applied to the scanner pods so that they can run on nodes with matching taints. Example: `'[{"key":"key1", "operator":"Equal", "value":"value1", "effect":"NoSchedule"}]'` |
+| `scanJob.annotations`           | N/A                                                    | One-line comma-separated representation of the annotations which the user wants the scan job's pods to be annotated with. Example: `foo=bar,env=stage` will annotate the scan job's pods with the annotations `foo: bar` and `env: stage` |
 
 | SECRET KEY                  | DESCRIPTION |
 | --------------------------- | ----------- |
diff --git a/pkg/cmd/scan_ciskubebench.go b/pkg/cmd/scan_ciskubebenchreports.go
similarity index 97%
rename from pkg/cmd/scan_ciskubebench.go
rename to pkg/cmd/scan_ciskubebenchreports.go
index d72a292..9a4f817 100644
--- a/pkg/cmd/scan_ciskubebench.go
+++ b/pkg/cmd/scan_ciskubebenchreports.go
@@ -59,7 +59,7 @@ func ScanKubeBenchReports(cf *genericclioptions.ConfigFlags) func(cmd *cobra.Com
 
 		scheme := starboard.NewScheme()
 		plugin := kubebench.NewKubeBenchPlugin(ext.NewSystemClock(), config)
-		scanner := kubebench.NewScanner(scheme, kubeClientset, opts, plugin)
+		scanner := kubebench.NewScanner(scheme, kubeClientset, plugin, config, opts)
 
 		kubeClient, err := client.New(kubeConfig, client.Options{
 			Scheme: scheme,
diff --git a/pkg/cmd/scan_configauditreports.go b/pkg/cmd/scan_configauditreports.go
index 2df1dac..d3c3d86 100644
--- a/pkg/cmd/scan_configauditreports.go
+++ b/pkg/cmd/scan_configauditreports.go
@@ -58,7 +58,7 @@ func ScanConfigAuditReports(buildInfo starboard.BuildInfo, cf *genericclioptions
 		if err != nil {
 			return err
 		}
-		starboardConfig, err := starboard.NewConfigManager(kubeClientset, starboard.NamespaceName).Read(ctx)
+		config, err := starboard.NewConfigManager(kubeClientset, starboard.NamespaceName).Read(ctx)
 		if err != nil {
 			return err
 		}
@@ -66,13 +66,13 @@ func ScanConfigAuditReports(buildInfo starboard.BuildInfo, cf *genericclioptions
 			WithBuildInfo(buildInfo).
 			WithNamespace(starboard.NamespaceName).
 			WithServiceAccountName(starboard.ServiceAccountName).
-			WithConfig(starboardConfig).
+			WithConfig(config).
 			WithClient(kubeClient).
 			GetConfigAuditPlugin()
 		if err != nil {
 			return err
 		}
-		scanner := configauditreport.NewScanner(kubeClientset, kubeClient, opts, plugin, pluginContext)
+		scanner := configauditreport.NewScanner(kubeClientset, kubeClient, plugin, pluginContext, config, opts)
 		report, err := scanner.Scan(ctx, workload)
 		if err != nil {
 			return err
diff --git a/pkg/cmd/scan_kubehunterreports.go b/pkg/cmd/scan_kubehunterreports.go
index b313a6f..3e08d07 100644
--- a/pkg/cmd/scan_kubehunterreports.go
+++ b/pkg/cmd/scan_kubehunterreports.go
@@ -50,7 +50,8 @@ func ScanKubeHunterReports(cf *genericclioptions.ConfigFlags) func(cmd *cobra.Co
 		if err != nil {
 			return err
 		}
-		report, err := kubehunter.NewScanner(starboard.NewScheme(), config, kubeClientset, opts).Scan(ctx)
+		scanner := kubehunter.NewScanner(starboard.NewScheme(), kubeClientset, config, opts)
+		report, err := scanner.Scan(ctx)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/cmd/scan_vulnerabilityreports.go b/pkg/cmd/scan_vulnerabilityreports.go
index b3b45f0..cc94a46 100644
--- a/pkg/cmd/scan_vulnerabilityreports.go
+++ b/pkg/cmd/scan_vulnerabilityreports.go
@@ -89,7 +89,7 @@ func ScanVulnerabilityReports(buildInfo starboard.BuildInfo, cf *genericclioptio
 		if err != nil {
 			return err
 		}
-		starboardConfig, err := starboard.NewConfigManager(kubeClientset, starboard.NamespaceName).Read(ctx)
+		config, err := starboard.NewConfigManager(kubeClientset, starboard.NamespaceName).Read(ctx)
 		if err != nil {
 			return err
 		}
@@ -101,13 +101,13 @@ func ScanVulnerabilityReports(buildInfo starboard.BuildInfo, cf *genericclioptio
 			WithBuildInfo(buildInfo).
 			WithNamespace(starboard.NamespaceName).
 			WithServiceAccountName(starboard.ServiceAccountName).
-			WithConfig(starboardConfig).
+			WithConfig(config).
 			WithClient(kubeClient).
 			GetVulnerabilityPlugin()
 		if err != nil {
 			return err
 		}
-		scanner := vulnerabilityreport.NewScanner(starboardConfig, kubeClientset, kubeClient, opts, plugin)
+		scanner := vulnerabilityreport.NewScanner(kubeClientset, kubeClient, plugin, config, opts)
 		reports, err := scanner.Scan(ctx, workload)
 		if err != nil {
 			return err
diff --git a/pkg/configauditreport/builder.go b/pkg/configauditreport/builder.go
index ade1cdb..e2012e1 100644
--- a/pkg/configauditreport/builder.go
+++ b/pkg/configauditreport/builder.go
@@ -18,10 +18,12 @@ import (
 )
 
 type ScanJobBuilder struct {
-	plugin        Plugin
-	pluginContext starboard.PluginContext
-	timeout       time.Duration
-	object        client.Object
+	plugin             Plugin
+	pluginContext      starboard.PluginContext
+	timeout            time.Duration
+	object             client.Object
+	tolerations        []corev1.Toleration
+	scanJobAnnotations map[string]string
 }
 
 func NewScanJob() *ScanJobBuilder {
@@ -48,6 +50,16 @@ func (s *ScanJobBuilder) WithObject(object client.Object) *ScanJobBuilder {
 	return s
 }
 
+func (s *ScanJobBuilder) WithTolerations(tolerations []corev1.Toleration) *ScanJobBuilder {
+	s.tolerations = tolerations
+	return s
+}
+
+func (s *ScanJobBuilder) WithScanJobAnnotations(scanJobAnnotations map[string]string) *ScanJobBuilder {
+	s.scanJobAnnotations = scanJobAnnotations
+	return s
+}
+
 func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 	jobSpec, secrets, err := s.plugin.GetScanJobSpec(s.pluginContext, s.object)
 	if err != nil {
@@ -59,6 +71,8 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 		return nil, nil, err
 	}
 
+	podSpec.Tolerations = append(podSpec.Tolerations, s.tolerations...)
+
 	podSpecHash := kube.ComputeHash(podSpec)
 
 	pluginConfigHash, err := s.plugin.GetConfigHash(s.pluginContext)
@@ -97,7 +111,8 @@ func (s *ScanJobBuilder) Get() (*batchv1.Job, []*corev1.Secret, error) {
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.timeout),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels: labels,
+					Labels:      labels,
+					Annotations: s.scanJobAnnotations,
 				},
 				Spec: jobSpec,
 			},
diff --git a/pkg/configauditreport/scanner.go b/pkg/configauditreport/scanner.go
index 1baab65..7035ebb 100644
--- a/pkg/configauditreport/scanner.go
+++ b/pkg/configauditreport/scanner.go
@@ -23,14 +23,16 @@ type Scanner struct {
 	logsReader     kube.LogsReader
 	plugin         Plugin
 	pluginContext  starboard.PluginContext
+	config         starboard.ConfigData
 }
 
 func NewScanner(
 	clientset kubernetes.Interface,
 	client client.Client,
-	opts kube.ScannerOpts,
 	plugin Plugin,
 	pluginContext starboard.PluginContext,
+	config starboard.ConfigData,
+	opts kube.ScannerOpts,
 ) *Scanner {
 	return &Scanner{
 		scheme:         client.Scheme(),
@@ -40,12 +42,21 @@ func NewScanner(
 		pluginContext:  pluginContext,
 		objectResolver: &kube.ObjectResolver{Client: client},
 		logsReader:     kube.NewLogsReader(clientset),
+		config:         config,
 	}
 }
 
 func (s *Scanner) Scan(ctx context.Context, workload kube.Object) (v1alpha1.ConfigAuditReport, error) {
 	klog.V(3).Infof("Getting Pod template for workload: %v", workload)
+	scanJobTolerations, err := s.config.GetScanJobTolerations()
+	if err != nil {
+		return v1alpha1.ConfigAuditReport{}, err
+	}
 
+	scanJobAnnotations, err := s.config.GetScanJobAnnotations()
+	if err != nil {
+		return v1alpha1.ConfigAuditReport{}, err
+	}
 	owner, err := s.objectResolver.GetObjectFromPartialObject(ctx, workload)
 	if err != nil {
 		return v1alpha1.ConfigAuditReport{}, err
@@ -57,6 +68,8 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.Object) (v1alpha1.Conf
 		WithPluginContext(s.pluginContext).
 		WithTimeout(s.opts.ScanJobTimeout).
 		WithObject(owner).
+		WithTolerations(scanJobTolerations).
+		WithScanJobAnnotations(scanJobAnnotations).
 		Get()
 	if err != nil {
 		return v1alpha1.ConfigAuditReport{}, err
diff --git a/pkg/kube/starboard.go b/pkg/kube/starboard.go
index 6b47673..020f798 100644
--- a/pkg/kube/starboard.go
+++ b/pkg/kube/starboard.go
@@ -5,6 +5,7 @@ import (
 )
 
 // ScannerOpts holds configuration of the vulnerability Scanner.
+// TODO Rename to CLIConfig and move it to the cmd package
 type ScannerOpts struct {
 	ScanJobTimeout time.Duration
 	DeleteScanJob  bool
diff --git a/pkg/kubebench/scanner.go b/pkg/kubebench/scanner.go
index 6355678..60d1d73 100644
--- a/pkg/kubebench/scanner.go
+++ b/pkg/kubebench/scanner.go
@@ -24,24 +24,27 @@ import (
 
 type Scanner struct {
 	scheme     *runtime.Scheme
-	opts       kube.ScannerOpts
 	clientset  kubernetes.Interface
 	logsReader kube.LogsReader
 	plugin     Plugin
+	config     starboard.ConfigData
+	opts       kube.ScannerOpts
 }
 
 func NewScanner(
 	scheme *runtime.Scheme,
 	clientset kubernetes.Interface,
-	opts kube.ScannerOpts,
 	plugin Plugin,
+	config starboard.ConfigData,
+	opts kube.ScannerOpts,
 ) *Scanner {
 	return &Scanner{
 		scheme:     scheme,
-		opts:       opts,
 		clientset:  clientset,
 		logsReader: kube.NewLogsReader(clientset),
 		plugin:     plugin,
+		config:     config,
+		opts:       opts,
 	}
 }
 
@@ -106,6 +109,17 @@ func (s *Scanner) prepareKubeBenchJob(node corev1.Node) (*batchv1.Job, error) {
 		return nil, err
 	}
 
+	scanJobTolerations, err := s.config.GetScanJobTolerations()
+	if err != nil {
+		return nil, err
+	}
+	templateSpec.Tolerations = append(templateSpec.Tolerations, scanJobTolerations...)
+
+	scanJobAnnotations, err := s.config.GetScanJobAnnotations()
+	if err != nil {
+		return nil, err
+	}
+
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "scan-cisbenchmark-" + kube.ComputeHash(node.Name),
@@ -125,6 +139,7 @@ func (s *Scanner) prepareKubeBenchJob(node corev1.Node) (*batchv1.Job, error) {
 						starboard.LabelResourceKind: string(kube.KindNode),
 						starboard.LabelResourceName: node.Name,
 					},
+					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
 			},
diff --git a/pkg/kubehunter/scanner.go b/pkg/kubehunter/scanner.go
index e2f3325..41ec7b3 100644
--- a/pkg/kubehunter/scanner.go
+++ b/pkg/kubehunter/scanner.go
@@ -31,26 +31,26 @@ type Config interface {
 
 type Scanner struct {
 	scheme    *runtime.Scheme
-	config    Config
 	clientset kubernetes.Interface
 	ext.IDGenerator
 	opts       kube.ScannerOpts
 	logsReader kube.LogsReader
+	config     starboard.ConfigData
 }
 
 func NewScanner(
 	scheme *runtime.Scheme,
-	config Config,
 	clientset kubernetes.Interface,
+	config starboard.ConfigData,
 	opts kube.ScannerOpts,
 ) *Scanner {
 	return &Scanner{
 		scheme:      scheme,
-		config:      config,
 		clientset:   clientset,
 		IDGenerator: ext.NewGoogleUUIDGenerator(),
-		opts:        opts,
 		logsReader:  kube.NewLogsReader(clientset),
+		config:      config,
+		opts:        opts,
 	}
 }
 
@@ -109,6 +109,16 @@ func (s *Scanner) prepareKubeHunterJob() (*batchv1.Job, error) {
 		kubeHunterArgs = append(kubeHunterArgs, "--quick")
 	}
 
+	scanJobTolerations, err := s.config.GetScanJobTolerations()
+	if err != nil {
+		return nil, err
+	}
+
+	scanJobAnnotations, err := s.config.GetScanJobAnnotations()
+	if err != nil {
+		return nil, err
+	}
+
 	var (
 		podSecurityContext       *corev1.PodSecurityContext
 		containerSecurityContext *corev1.SecurityContext
@@ -146,11 +156,15 @@ func (s *Scanner) prepareKubeHunterJob() (*batchv1.Job, error) {
 			Completions:           pointer.Int32Ptr(1),
 			ActiveDeadlineSeconds: kube.GetActiveDeadlineSeconds(s.opts.ScanJobTimeout),
 			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Annotations: scanJobAnnotations,
+				},
 				Spec: corev1.PodSpec{
 					ServiceAccountName: starboard.ServiceAccountName,
 					RestartPolicy:      corev1.RestartPolicyNever,
 					HostPID:            true,
 					Affinity:           starboard.LinuxNodeAffinity(),
+					Tolerations:        scanJobTolerations,
 					SecurityContext:    podSecurityContext,
 					Containers: []corev1.Container{
 						{
diff --git a/pkg/operator/controller/ciskubebenchreport.go b/pkg/operator/controller/ciskubebenchreport.go
index 85ab9ad..ab08bae 100644
--- a/pkg/operator/controller/ciskubebenchreport.go
+++ b/pkg/operator/controller/ciskubebenchreport.go
@@ -42,6 +42,7 @@ type CISKubeBenchReportReconciler struct {
 	LimitChecker
 	kubebench.ReadWriter
 	kubebench.Plugin
+	starboard.ConfigData
 }
 
 func (r *CISKubeBenchReportReconciler) SetupWithManager(mgr ctrl.Manager) error {
@@ -158,6 +159,17 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 
 	templateSpec.ServiceAccountName = r.Config.ServiceAccount
 
+	scanJobTolerations, err := r.ConfigData.GetScanJobTolerations()
+	if err != nil {
+		return nil, err
+	}
+	templateSpec.Tolerations = append(templateSpec.Tolerations, scanJobTolerations...)
+
+	scanJobAnnotations, err := r.ConfigData.GetScanJobAnnotations()
+	if err != nil {
+		return nil, err
+	}
+
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      r.getScanJobName(node),
@@ -181,6 +193,7 @@ func (r *CISKubeBenchReportReconciler) newScanJob(node *corev1.Node) (*batchv1.J
 						starboard.LabelK8SAppManagedBy:        starboard.AppStarboard,
 						starboard.LabelKubeBenchReportScanner: "true",
 					},
+					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
 			},
diff --git a/pkg/operator/controller/configauditreport.go b/pkg/operator/controller/configauditreport.go
index 4316a7e..f7768ab 100644
--- a/pkg/operator/controller/configauditreport.go
+++ b/pkg/operator/controller/configauditreport.go
@@ -28,6 +28,7 @@ import (
 type ConfigAuditReportReconciler struct {
 	logr.Logger
 	etc.Config
+	starboard.ConfigData
 	client.Client
 	kube.ObjectResolver
 	LimitChecker
@@ -160,11 +161,23 @@ func (r *ConfigAuditReportReconciler) reconcileWorkload(workloadKind kube.Kind)
 			return ctrl.Result{RequeueAfter: r.Config.ScanJobRetryAfter}, nil
 		}
 
+		scanJobTolerations, err := r.ConfigData.GetScanJobTolerations()
+		if err != nil {
+			return ctrl.Result{}, err
+		}
+
+		scanJobAnnotations, err := r.ConfigData.GetScanJobAnnotations()
+		if err != nil {
+			return ctrl.Result{}, err
+		}
+
 		job, secrets, err := configauditreport.NewScanJob().
 			WithPlugin(r.Plugin).
 			WithPluginContext(r.PluginContext).
 			WithTimeout(r.Config.ScanJobTimeout).
 			WithObject(workloadObj).
+			WithTolerations(scanJobTolerations).
+			WithScanJobAnnotations(scanJobAnnotations).
 			Get()
 		if err != nil {
 			return ctrl.Result{}, err
diff --git a/pkg/operator/controller/vulnerabilityreport.go b/pkg/operator/controller/vulnerabilityreport.go
index f4f148a..5409067 100644
--- a/pkg/operator/controller/vulnerabilityreport.go
+++ b/pkg/operator/controller/vulnerabilityreport.go
@@ -222,6 +222,11 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, spec
 	}
 	templateSpec.Tolerations = append(templateSpec.Tolerations, scanJobTolerations...)
 
+	scanJobAnnotations, err := r.ConfigData.GetScanJobAnnotations()
+	if err != nil {
+		return err
+	}
+
 	containerImagesAsJSON, err := images.AsJSON()
 	if err != nil {
 		return err
@@ -269,6 +274,7 @@ func (r *VulnerabilityReportReconciler) submitScanJob(ctx context.Context, spec
 						starboard.LabelK8SAppManagedBy:            starboard.AppStarboard,
 						starboard.LabelVulnerabilityReportScanner: "true",
 					},
+					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
 			},
diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go
index b30fd1c..aac95ee 100644
--- a/pkg/operator/operator.go
+++ b/pkg/operator/operator.go
@@ -171,6 +171,7 @@ func Start(ctx context.Context, buildInfo starboard.BuildInfo, operatorConfig et
 		if err = (&controller.ConfigAuditReportReconciler{
 			Logger:         ctrl.Log.WithName("reconciler").WithName("configauditreport"),
 			Config:         operatorConfig,
+			ConfigData:     starboardConfig,
 			Client:         mgr.GetClient(),
 			ObjectResolver: objectResolver,
 			LimitChecker:   limitChecker,
@@ -197,6 +198,7 @@ func Start(ctx context.Context, buildInfo starboard.BuildInfo, operatorConfig et
 		if err = (&controller.CISKubeBenchReportReconciler{
 			Logger:       ctrl.Log.WithName("reconciler").WithName("ciskubebenchreport"),
 			Config:       operatorConfig,
+			ConfigData:   starboardConfig,
 			Client:       mgr.GetClient(),
 			LogsReader:   logsReader,
 			LimitChecker: limitChecker,
diff --git a/pkg/plugin/factory.go b/pkg/plugin/factory.go
index dec0895..bf4824e 100644
--- a/pkg/plugin/factory.go
+++ b/pkg/plugin/factory.go
@@ -66,7 +66,7 @@ func (r *Resolver) GetVulnerabilityPlugin() (vulnerabilityreport.Plugin, error)
 	}
 	switch scanner {
 	case starboard.Trivy:
-		return trivy.NewPlugin(ext.NewGoogleUUIDGenerator(), r.config), nil
+		return trivy.NewPlugin(ext.NewSystemClock(), ext.NewGoogleUUIDGenerator(), r.config), nil
 	case starboard.Aqua:
 		return aqua.NewPlugin(ext.NewGoogleUUIDGenerator(), r.buildInfo, r.config), nil
 	}
diff --git a/pkg/plugin/trivy/converter.go b/pkg/plugin/trivy/converter.go
deleted file mode 100644
index c9cd676..0000000
--- a/pkg/plugin/trivy/converter.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package trivy
-
-import (
-	"encoding/json"
-	"io"
-	"time"
-
-	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
-	"github.com/aquasecurity/starboard/pkg/starboard"
-	"github.com/google/go-containerregistry/pkg/name"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-// Converter is the interface that wraps the Convert method.
-//
-// Convert converts the vulnerabilities model used by Trivy
-// to a generic model defined by the Custom Security Resource Specification.
-type Converter interface {
-	Convert(imageRef string, reader io.Reader) (v1alpha1.VulnerabilityScanResult, error)
-}
-
-type converter struct {
-	config Config
-}
-
-func NewConverter(config Config) Converter {
-	return &converter{
-		config: config,
-	}
-}
-
-func (c *converter) Convert(imageRef string, reader io.Reader) (v1alpha1.VulnerabilityScanResult, error) {
-	var scanReports []ScanReport
-	err := json.NewDecoder(reader).Decode(&scanReports)
-	if err != nil {
-		return v1alpha1.VulnerabilityScanResult{}, err
-	}
-	return c.convert(imageRef, scanReports)
-}
-
-func (c *converter) convert(imageRef string, reports []ScanReport) (v1alpha1.VulnerabilityScanResult, error) {
-	vulnerabilities := make([]v1alpha1.Vulnerability, 0)
-
-	for _, report := range reports {
-		for _, sr := range report.Vulnerabilities {
-			vulnerabilities = append(vulnerabilities, v1alpha1.Vulnerability{
-				VulnerabilityID:  sr.VulnerabilityID,
-				Resource:         sr.PkgName,
-				InstalledVersion: sr.InstalledVersion,
-				FixedVersion:     sr.FixedVersion,
-				Severity:         sr.Severity,
-				Title:            sr.Title,
-				PrimaryLink:      sr.PrimaryURL,
-				Links:            []string{},
-				Score:            GetScoreFromCvss(sr.Cvss),
-			})
-		}
-	}
-
-	registry, artifact, err := c.parseImageRef(imageRef)
-	if err != nil {
-		return v1alpha1.VulnerabilityScanResult{}, err
-	}
-
-	trivyImageRef, err := c.config.GetTrivyImageRef()
-	if err != nil {
-		return v1alpha1.VulnerabilityScanResult{}, err
-	}
-
-	version, err := starboard.GetVersionFromImageRef(trivyImageRef)
-	if err != nil {
-		return v1alpha1.VulnerabilityScanResult{}, err
-	}
-
-	return v1alpha1.VulnerabilityScanResult{
-		UpdateTimestamp: metav1.NewTime(time.Now()),
-		Scanner: v1alpha1.Scanner{
-			Name:    "Trivy",
-			Vendor:  "Aqua Security",
-			Version: version,
-		},
-		Registry:        registry,
-		Artifact:        artifact,
-		Summary:         c.toSummary(vulnerabilities),
-		Vulnerabilities: vulnerabilities,
-	}, nil
-}
-
-func (c *converter) toSummary(vulnerabilities []v1alpha1.Vulnerability) (vs v1alpha1.VulnerabilitySummary) {
-	for _, v := range vulnerabilities {
-		switch v.Severity {
-		case v1alpha1.SeverityCritical:
-			vs.CriticalCount++
-		case v1alpha1.SeverityHigh:
-			vs.HighCount++
-		case v1alpha1.SeverityMedium:
-			vs.MediumCount++
-		case v1alpha1.SeverityLow:
-			vs.LowCount++
-		default:
-			vs.UnknownCount++
-		}
-	}
-	return
-}
-
-// TODO check if it works if both tag and digest are specified
-func (c *converter) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Artifact, error) {
-	ref, err := name.ParseReference(imageRef)
-	if err != nil {
-		return v1alpha1.Registry{}, v1alpha1.Artifact{}, err
-	}
-	registry := v1alpha1.Registry{
-		Server: ref.Context().RegistryStr(),
-	}
-	artifact := v1alpha1.Artifact{
-		Repository: ref.Context().RepositoryStr(),
-	}
-	switch t := ref.(type) {
-	case name.Tag:
-		artifact.Tag = t.TagStr()
-	case name.Digest:
-		artifact.Digest = t.DigestStr()
-	}
-
-	return registry, artifact, nil
-}
-
-func GetScoreFromCvss(CVSSs map[string]*CVSS) *float64 {
-	var nvdScore, vendorScore *float64
-
-	for name, cvss := range CVSSs {
-		if name == "nvd" {
-			nvdScore = cvss.V3Score
-		} else {
-			vendorScore = cvss.V3Score
-		}
-	}
-
-	if vendorScore != nil {
-		return vendorScore
-	}
-
-	return nvdScore
-}
diff --git a/pkg/plugin/trivy/plugin.go b/pkg/plugin/trivy/plugin.go
index 56d0c86..d87c64a 100644
--- a/pkg/plugin/trivy/plugin.go
+++ b/pkg/plugin/trivy/plugin.go
@@ -1,6 +1,7 @@
 package trivy
 
 import (
+	"encoding/json"
 	"fmt"
 	"io"
 
@@ -30,10 +31,11 @@ var (
 	}
 )
 
+// TODO Rename scanner struct to plugin.
 type scanner struct {
+	clock       ext.Clock
 	idGenerator ext.IDGenerator
 	config      Config
-	converter   Converter
 }
 
 // Config defines configuration params for the Trivy vulnerabilityreport.Plugin.
@@ -53,11 +55,11 @@ type Config interface {
 //
 // The starboard.ClientServer mode is usually more performant, however it
 // requires a Trivy server accessible at the configurable URL.
-func NewPlugin(idGenerator ext.IDGenerator, config Config) vulnerabilityreport.Plugin {
+func NewPlugin(clock ext.Clock, idGenerator ext.IDGenerator, config Config) vulnerabilityreport.Plugin {
 	return &scanner{
+		clock:       clock,
 		idGenerator: idGenerator,
 		config:      config,
-		converter:   NewConverter(config),
 	}
 }
 
@@ -660,14 +662,6 @@ func (s *scanner) getPodSpecForClientServerMode(spec corev1.PodSpec, credentials
 	}, secrets, nil
 }
 
-func (s *scanner) ParseVulnerabilityScanResult(imageRef string, logsReader io.ReadCloser) (v1alpha1.VulnerabilityScanResult, error) {
-	result, err := s.converter.Convert(imageRef, logsReader)
-	if err != nil {
-		return v1alpha1.VulnerabilityScanResult{}, err
-	}
-	return result, nil
-}
-
 func (s *scanner) appendTrivyInsecureEnv(image string, env []corev1.EnvVar) ([]corev1.EnvVar, error) {
 	ref, err := name.ParseReference(image)
 	if err != nil {
@@ -684,3 +678,114 @@ func (s *scanner) appendTrivyInsecureEnv(image string, env []corev1.EnvVar) ([]c
 
 	return env, nil
 }
+
+func (s *scanner) ParseVulnerabilityScanResult(imageRef string, logsReader io.ReadCloser) (v1alpha1.VulnerabilityScanResult, error) {
+	var reports []ScanReport
+	err := json.NewDecoder(logsReader).Decode(&reports)
+	if err != nil {
+		return v1alpha1.VulnerabilityScanResult{}, err
+	}
+	vulnerabilities := make([]v1alpha1.Vulnerability, 0)
+
+	for _, report := range reports {
+		for _, sr := range report.Vulnerabilities {
+			vulnerabilities = append(vulnerabilities, v1alpha1.Vulnerability{
+				VulnerabilityID:  sr.VulnerabilityID,
+				Resource:         sr.PkgName,
+				InstalledVersion: sr.InstalledVersion,
+				FixedVersion:     sr.FixedVersion,
+				Severity:         sr.Severity,
+				Title:            sr.Title,
+				PrimaryLink:      sr.PrimaryURL,
+				Links:            []string{},
+				Score:            GetScoreFromCVSS(sr.Cvss),
+			})
+		}
+	}
+
+	registry, artifact, err := s.parseImageRef(imageRef)
+	if err != nil {
+		return v1alpha1.VulnerabilityScanResult{}, err
+	}
+
+	trivyImageRef, err := s.config.GetTrivyImageRef()
+	if err != nil {
+		return v1alpha1.VulnerabilityScanResult{}, err
+	}
+
+	version, err := starboard.GetVersionFromImageRef(trivyImageRef)
+	if err != nil {
+		return v1alpha1.VulnerabilityScanResult{}, err
+	}
+
+	return v1alpha1.VulnerabilityScanResult{
+		UpdateTimestamp: metav1.NewTime(s.clock.Now()),
+		Scanner: v1alpha1.Scanner{
+			Name:    "Trivy",
+			Vendor:  "Aqua Security",
+			Version: version,
+		},
+		Registry:        registry,
+		Artifact:        artifact,
+		Summary:         s.toSummary(vulnerabilities),
+		Vulnerabilities: vulnerabilities,
+	}, nil
+}
+
+func (s *scanner) toSummary(vulnerabilities []v1alpha1.Vulnerability) (vs v1alpha1.VulnerabilitySummary) {
+	for _, v := range vulnerabilities {
+		switch v.Severity {
+		case v1alpha1.SeverityCritical:
+			vs.CriticalCount++
+		case v1alpha1.SeverityHigh:
+			vs.HighCount++
+		case v1alpha1.SeverityMedium:
+			vs.MediumCount++
+		case v1alpha1.SeverityLow:
+			vs.LowCount++
+		default:
+			vs.UnknownCount++
+		}
+	}
+	return
+}
+
+// TODO check if it works if both tag and digest are specified
+func (s *scanner) parseImageRef(imageRef string) (v1alpha1.Registry, v1alpha1.Artifact, error) {
+	ref, err := name.ParseReference(imageRef)
+	if err != nil {
+		return v1alpha1.Registry{}, v1alpha1.Artifact{}, err
+	}
+	registry := v1alpha1.Registry{
+		Server: ref.Context().RegistryStr(),
+	}
+	artifact := v1alpha1.Artifact{
+		Repository: ref.Context().RepositoryStr(),
+	}
+	switch t := ref.(type) {
+	case name.Tag:
+		artifact.Tag = t.TagStr()
+	case name.Digest:
+		artifact.Digest = t.DigestStr()
+	}
+
+	return registry, artifact, nil
+}
+
+func GetScoreFromCVSS(CVSSs map[string]*CVSS) *float64 {
+	var nvdScore, vendorScore *float64
+
+	for name, cvss := range CVSSs {
+		if name == "nvd" {
+			nvdScore = cvss.V3Score
+		} else {
+			vendorScore = cvss.V3Score
+		}
+	}
+
+	if vendorScore != nil {
+		return vendorScore
+	}
+
+	return nvdScore
+}
diff --git a/pkg/starboard/config.go b/pkg/starboard/config.go
index 8988bd9..2f0c59c 100644
--- a/pkg/starboard/config.go
+++ b/pkg/starboard/config.go
@@ -364,6 +364,25 @@ func (c ConfigData) GetTrivyMode() (TrivyMode, error) {
 		value, keyTrivyMode, Standalone, ClientServer)
 }
 
+func (c ConfigData) GetScanJobAnnotations() (map[string]string, error) {
+	scanJobAnnotationsStr, found := c[AnnotationScanJobAnnotations]
+	if !found || strings.TrimSpace(scanJobAnnotationsStr) == "" {
+		return map[string]string{}, nil
+	}
+
+	scanJobAnnotationsMap := map[string]string{}
+	for _, annotation := range strings.Split(scanJobAnnotationsStr, ",") {
+		sepByEqual := strings.Split(annotation, "=")
+		if len(sepByEqual) != 2 {
+			return map[string]string{}, fmt.Errorf("custom annotations found to be wrongfully provided: %s", scanJobAnnotationsStr)
+		}
+		key, value := sepByEqual[0], sepByEqual[1]
+		scanJobAnnotationsMap[key] = value
+	}
+
+	return scanJobAnnotationsMap, nil
+}
+
 func (c ConfigData) GetTrivyServerURL() (string, error) {
 	return c.getRequiredProperty(keyTrivyServerURL)
 }
@@ -463,7 +482,6 @@ func (c *configManager) EnsureDefault(ctx context.Context) error {
 		if !apierrors.IsNotFound(err) {
 			return fmt.Errorf("getting config: %w", err)
 		}
-
 		cm, err = c.client.CoreV1().ConfigMaps(c.namespace).Create(ctx, &corev1.ConfigMap{
 			ObjectMeta: metav1.ObjectMeta{
 				Namespace: c.namespace,
diff --git a/pkg/starboard/constants.go b/pkg/starboard/constants.go
index ecf936e..5dfdc18 100644
--- a/pkg/starboard/constants.go
+++ b/pkg/starboard/constants.go
@@ -35,5 +35,6 @@ const (
 )
 
 const (
-	AnnotationContainerImages = "starboard.container-images"
+	AnnotationContainerImages    = "starboard.container-images"
+	AnnotationScanJobAnnotations = "scanJob.annotations"
 )
diff --git a/pkg/vulnerabilityreport/scanner.go b/pkg/vulnerabilityreport/scanner.go
index 918840b..20ee3e1 100644
--- a/pkg/vulnerabilityreport/scanner.go
+++ b/pkg/vulnerabilityreport/scanner.go
@@ -3,6 +3,7 @@ package vulnerabilityreport
 import (
 	"context"
 	"fmt"
+
 	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"github.com/aquasecurity/starboard/pkg/docker"
 	"github.com/aquasecurity/starboard/pkg/ext"
@@ -22,15 +23,15 @@ import (
 // Scanner is a template for running static vulnerability scanners that implement
 // the Plugin interface.
 type Scanner struct {
-	scheme          *runtime.Scheme
-	clientset       kubernetes.Interface
-	opts            kube.ScannerOpts
-	plugin          Plugin
-	objectResolver  *kube.ObjectResolver
-	logsReader      kube.LogsReader
-	starboardConfig starboard.ConfigData
-	ext.IDGenerator
-	kube.SecretsReader
+	scheme         *runtime.Scheme
+	clientset      kubernetes.Interface
+	plugin         Plugin
+	objectResolver *kube.ObjectResolver
+	logsReader     kube.LogsReader
+	config         starboard.ConfigData
+	opts           kube.ScannerOpts
+	idGenerator    ext.IDGenerator
+	secretsReader  kube.SecretsReader
 }
 
 // NewScanner constructs a new static vulnerability Scanner with the specified
@@ -38,22 +39,22 @@ type Scanner struct {
 // which is performed by running a Kubernetes job, and knows how to convert logs
 // to instances of v1alpha1.VulnerabilityReport.
 func NewScanner(
-	starboardConfig starboard.ConfigData,
 	clientset kubernetes.Interface,
 	client client.Client,
-	opts kube.ScannerOpts,
 	plugin Plugin,
+	config starboard.ConfigData,
+	opts kube.ScannerOpts,
 ) *Scanner {
 	return &Scanner{
-		scheme:          client.Scheme(),
-		clientset:       clientset,
-		opts:            opts,
-		plugin:          plugin,
-		objectResolver:  &kube.ObjectResolver{Client: client},
-		logsReader:      kube.NewLogsReader(clientset),
-		starboardConfig: starboardConfig,
-		IDGenerator:     ext.NewGoogleUUIDGenerator(),
-		SecretsReader:   kube.NewSecretsReader(client),
+		scheme:         client.Scheme(),
+		clientset:      clientset,
+		opts:           opts,
+		plugin:         plugin,
+		objectResolver: &kube.ObjectResolver{Client: client},
+		logsReader:     kube.NewLogsReader(clientset),
+		config:         config,
+		idGenerator:    ext.NewGoogleUUIDGenerator(),
+		secretsReader:  kube.NewSecretsReader(client),
 	}
 }
 
@@ -83,7 +84,7 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.Object) ([]v1alpha1.Vu
 		return nil, err
 	}
 
-	job, secrets, err := s.prepareScanJob(workload, spec, credentials, s.starboardConfig)
+	job, secrets, err := s.prepareScanJob(workload, spec, credentials)
 	if err != nil {
 		return nil, fmt.Errorf("preparing scan job: %w", err)
 	}
@@ -111,20 +112,20 @@ func (s *Scanner) Scan(ctx context.Context, workload kube.Object) ([]v1alpha1.Vu
 }
 
 func (s *Scanner) getCredentials(ctx context.Context, spec corev1.PodSpec, ns string) (map[string]docker.Auth, error) {
-	imagePullSecrets, err := s.ListImagePullSecretsByPodSpec(ctx, spec, ns)
+	imagePullSecrets, err := s.secretsReader.ListImagePullSecretsByPodSpec(ctx, spec, ns)
 	if err != nil {
 		return nil, err
 	}
 	return kube.MapContainerNamesToDockerAuths(kube.GetContainerImagesFromPodSpec(spec), imagePullSecrets)
 }
 
-func (s *Scanner) prepareScanJob(workload kube.Object, spec corev1.PodSpec, credentials map[string]docker.Auth, starboardConfigData starboard.ConfigData) (*batchv1.Job, []*corev1.Secret, error) {
+func (s *Scanner) prepareScanJob(workload kube.Object, spec corev1.PodSpec, credentials map[string]docker.Auth) (*batchv1.Job, []*corev1.Secret, error) {
 	templateSpec, secrets, err := s.plugin.GetScanJobSpec(spec, credentials)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	scanJobTolerations, err := starboardConfigData.GetScanJobTolerations()
+	scanJobTolerations, err := s.config.GetScanJobTolerations()
 	if err != nil {
 		return nil, nil, err
 	}
@@ -137,9 +138,14 @@ func (s *Scanner) prepareScanJob(workload kube.Object, spec corev1.PodSpec, cred
 		return nil, nil, err
 	}
 
+	scanJobAnnotations, err := s.config.GetScanJobAnnotations()
+	if err != nil {
+		return nil, nil, err
+	}
+
 	return &batchv1.Job{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      s.GenerateID(),
+			Name:      s.idGenerator.GenerateID(),
 			Namespace: starboard.NamespaceName,
 			Labels: map[string]string{
 				starboard.LabelResourceKind:      string(workload.Kind),
@@ -161,6 +167,7 @@ func (s *Scanner) prepareScanJob(workload kube.Object, spec corev1.PodSpec, cred
 						starboard.LabelResourceName:      workload.Name,
 						starboard.LabelResourceNamespace: workload.Namespace,
 					},
+					Annotations: scanJobAnnotations,
 				},
 				Spec: templateSpec,
 			},


Test patch:
diff --git a/itest/starboard/starboard_cli_test.go b/itest/starboard/starboard_cli_test.go
index edab6b0..399c153 100644
--- a/itest/starboard/starboard_cli_test.go
+++ b/itest/starboard/starboard_cli_test.go
@@ -693,13 +693,14 @@ var _ = Describe("Starboard CLI", func() {
 				var report *v1alpha1.VulnerabilityReport
 
 				BeforeEach(func() {
-					deploy = helper.NewDeployment().WithName("nginx").
+					deploy = helper.NewDeployment().WithRandomName("nginx").
 						WithNamespace(testNamespace.Name).
 						WithContainer("nginx", "nginx:1.16").
 						Build()
 					err := kubeClient.Create(context.TODO(), deploy)
 					Expect(err).ToNot(HaveOccurred())
 
+					replicasetName = ""
 					for i := 0; i < 10; i++ {
 						var rsList appsv1.ReplicaSetList
 						err := kubeClient.List(context.TODO(), &rsList)
@@ -720,6 +721,7 @@ var _ = Describe("Starboard CLI", func() {
 					}
 					Expect(replicasetName).ToNot(BeEmpty())
 
+					podName = ""
 					for i := 0; i < 10; i++ {
 						var podList corev1.PodList
 						err := kubeClient.List(context.TODO(), &podList)
diff --git a/pkg/configauditreport/plugin_test.go b/pkg/configauditreport/plugin_test.go
new file mode 100644
index 0000000..229879a
--- /dev/null
+++ b/pkg/configauditreport/plugin_test.go
@@ -0,0 +1,1 @@
+package configauditreport
diff --git a/pkg/kubehunter/model_test.go b/pkg/kubehunter/model_test.go
new file mode 100644
index 0000000..becbe0e
--- /dev/null
+++ b/pkg/kubehunter/model_test.go
@@ -0,0 +1,1 @@
+package kubehunter_test
diff --git a/pkg/kubehunter/scanner_test.go b/pkg/kubehunter/scanner_test.go
new file mode 100644
index 0000000..becbe0e
--- /dev/null
+++ b/pkg/kubehunter/scanner_test.go
@@ -0,0 +1,1 @@
+package kubehunter_test
diff --git a/pkg/plugin/trivy/converter_test.go b/pkg/plugin/trivy/converter_test.go
deleted file mode 100644
index ba94f85..0000000
--- a/pkg/plugin/trivy/converter_test.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package trivy_test
-
-import (
-	"errors"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
-	"github.com/aquasecurity/starboard/pkg/plugin/trivy"
-	"github.com/aquasecurity/starboard/pkg/starboard"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/utils/pointer"
-)
-
-var (
-	sampleReportAsString = `[{
-		"Target": "alpine:3.10.2 (alpine 3.10.2)",
-		"Type": "alpine",
-		"Vulnerabilities": [
-			{
-				"VulnerabilityID": "CVE-2019-1549",
-				"PkgName": "openssl",
-				"InstalledVersion": "1.1.1c-r0",
-				"FixedVersion": "1.1.1d-r0",
-				"Title": "openssl: information disclosure in fork()",
-				"Description": "Usually this long long description of CVE-2019-1549",
-				"Severity": "MEDIUM",
-				"PrimaryURL": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549",
-				"References": [
-					"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549"
-				]
-			},
-			{
-				"VulnerabilityID": "CVE-2019-1547",
-				"PkgName": "openssl",
-				"InstalledVersion": "1.1.1c-r0",
-				"FixedVersion": "1.1.1d-r0",
-				"Title": "openssl: side-channel weak encryption vulnerability",
-				"Severity": "LOW",
-				"PrimaryURL": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
-				"References": [
-					"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547"
-				]
-			}
-		]
-	}]`
-
-	sampleReport = v1alpha1.VulnerabilityScanResult{
-		Scanner: v1alpha1.Scanner{
-			Name:    "Trivy",
-			Vendor:  "Aqua Security",
-			Version: "0.9.1",
-		},
-		Registry: v1alpha1.Registry{
-			Server: "index.docker.io",
-		},
-		Artifact: v1alpha1.Artifact{
-			Repository: "library/alpine",
-			Tag:        "3.10.2",
-		},
-		Summary: v1alpha1.VulnerabilitySummary{
-			CriticalCount: 0,
-			MediumCount:   1,
-			LowCount:      1,
-			NoneCount:     0,
-			UnknownCount:  0,
-		},
-		Vulnerabilities: []v1alpha1.Vulnerability{
-			{
-				VulnerabilityID:  "CVE-2019-1549",
-				Resource:         "openssl",
-				InstalledVersion: "1.1.1c-r0",
-				FixedVersion:     "1.1.1d-r0",
-				Severity:         v1alpha1.SeverityMedium,
-				Title:            "openssl: information disclosure in fork()",
-				PrimaryLink:      "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549",
-				Links:            []string{},
-			},
-			{
-				VulnerabilityID:  "CVE-2019-1547",
-				Resource:         "openssl",
-				InstalledVersion: "1.1.1c-r0",
-				FixedVersion:     "1.1.1d-r0",
-				Severity:         v1alpha1.SeverityLow,
-				Title:            "openssl: side-channel weak encryption vulnerability",
-				PrimaryLink:      "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
-				Links:            []string{},
-			},
-		},
-	}
-)
-
-func TestConverter_Convert(t *testing.T) {
-	config := starboard.ConfigData{
-		"trivy.imageRef": "aquasec/trivy:0.9.1",
-	}
-
-	testCases := []struct {
-		name           string
-		imageRef       string
-		input          string
-		expectedError  error
-		expectedReport v1alpha1.VulnerabilityScanResult
-	}{
-		{
-			name:           "Should convert vulnerability report in JSON format when input is quiet",
-			imageRef:       "alpine:3.10.2",
-			input:          sampleReportAsString,
-			expectedError:  nil,
-			expectedReport: sampleReport,
-		},
-		{
-			name:          "Should convert vulnerability report in JSON format when OS is not detected",
-			imageRef:      "core.harbor.domain/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c",
-			input:         `null`,
-			expectedError: nil,
-			expectedReport: v1alpha1.VulnerabilityScanResult{
-				Scanner: v1alpha1.Scanner{
-					Name:    "Trivy",
-					Vendor:  "Aqua Security",
-					Version: "0.9.1",
-				},
-				Registry: v1alpha1.Registry{
-					Server: "core.harbor.domain",
-				},
-				Artifact: v1alpha1.Artifact{
-					Repository: "library/nginx",
-					Digest:     "sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c",
-				},
-				Summary: v1alpha1.VulnerabilitySummary{
-					CriticalCount: 0,
-					HighCount:     0,
-					MediumCount:   0,
-					LowCount:      0,
-					NoneCount:     0,
-					UnknownCount:  0,
-				},
-				Vulnerabilities: []v1alpha1.Vulnerability{},
-			},
-		},
-		{
-			name:          "Should return error when image reference cannot be parsed",
-			imageRef:      ":",
-			input:         "null",
-			expectedError: errors.New("could not parse reference: :"),
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			report, err := trivy.NewConverter(config).Convert(tc.imageRef, strings.NewReader(tc.input))
-			fakeTime := metav1.NewTime(time.Now())
-			report.UpdateTimestamp = fakeTime
-			tc.expectedReport.UpdateTimestamp = fakeTime
-			switch {
-			case tc.expectedError == nil:
-				require.NoError(t, err)
-				assert.Equal(t, tc.expectedReport, report)
-			default:
-				assert.EqualError(t, err, tc.expectedError.Error())
-			}
-		})
-	}
-
-}
-
-func TestGetScoreFromCvss(t *testing.T) {
-	testCases := []struct {
-		name          string
-		cvss          map[string]*trivy.CVSS
-		expectedScore *float64
-	}{
-		{
-			name: "Should return vendor score when vendor v3 score exist",
-			cvss: map[string]*trivy.CVSS{
-				"nvd": {
-					V3Score: pointer.Float64Ptr(8.1),
-				},
-				"redhat": {
-					V3Score: pointer.Float64Ptr(8.3),
-				},
-			},
-			expectedScore: pointer.Float64Ptr(8.3),
-		},
-		{
-			name: "Should return nvd score when vendor v3 score is nil",
-			cvss: map[string]*trivy.CVSS{
-				"nvd": {
-					V3Score: pointer.Float64Ptr(8.1),
-				},
-				"redhat": {
-					V3Score: nil,
-				},
-			},
-			expectedScore: pointer.Float64Ptr(8.1),
-		},
-		{
-			name: "Should return nvd score when vendor doesn't exist",
-			cvss: map[string]*trivy.CVSS{
-				"nvd": {
-					V3Score: pointer.Float64Ptr(8.1),
-				},
-			},
-			expectedScore: pointer.Float64Ptr(8.1),
-		},
-		{
-			name: "Should return nil when vendor and nvd both v3 scores are nil",
-			cvss: map[string]*trivy.CVSS{
-				"nvd": {
-					V3Score: nil,
-				},
-				"redhat": {
-					V3Score: nil,
-				},
-			},
-			expectedScore: nil,
-		},
-		{
-			name:          "Should return nil when cvss doesn't exist",
-			cvss:          map[string]*trivy.CVSS{},
-			expectedScore: nil,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			score := trivy.GetScoreFromCvss(tc.cvss)
-			assert.Equal(t, tc.expectedScore, score)
-		})
-	}
-}
diff --git a/pkg/plugin/trivy/plugin_test.go b/pkg/plugin/trivy/plugin_test.go
index cbeda12..553205d 100644
--- a/pkg/plugin/trivy/plugin_test.go
+++ b/pkg/plugin/trivy/plugin_test.go
@@ -1,8 +1,13 @@
 package trivy_test
 
 import (
+	"errors"
+	"io"
+	"strings"
 	"testing"
+	"time"
 
+	"github.com/aquasecurity/starboard/pkg/apis/aquasecurity/v1alpha1"
 	"github.com/aquasecurity/starboard/pkg/ext"
 	"github.com/aquasecurity/starboard/pkg/plugin/trivy"
 	"github.com/aquasecurity/starboard/pkg/starboard"
@@ -10,9 +15,15 @@ import (
 	"github.com/stretchr/testify/require"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/utils/pointer"
 )
 
+var (
+	fixedTime  = time.Now()
+	fixedClock = ext.NewFixedClock(fixedTime)
+)
+
 func TestScanner_GetScanJobSpec(t *testing.T) {
 
 	testCases := []struct {
@@ -1346,8 +1357,8 @@ CVE-2019-1543`,
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			jobSpec, secrets, err := trivy.NewPlugin(ext.NewSimpleIDGenerator(), tc.config).
-				GetScanJobSpec(tc.workloadSpec, nil)
+			instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), tc.config)
+			jobSpec, secrets, err := instance.GetScanJobSpec(tc.workloadSpec, nil)
 			require.NoError(t, err)
 			assert.Empty(t, secrets)
 			assert.Equal(t, tc.expectedJobSpec, jobSpec)
@@ -1355,3 +1366,221 @@ CVE-2019-1543`,
 	}
 
 }
+
+var (
+	sampleReportAsString = `[{
+		"Target": "alpine:3.10.2 (alpine 3.10.2)",
+		"Type": "alpine",
+		"Vulnerabilities": [
+			{
+				"VulnerabilityID": "CVE-2019-1549",
+				"PkgName": "openssl",
+				"InstalledVersion": "1.1.1c-r0",
+				"FixedVersion": "1.1.1d-r0",
+				"Title": "openssl: information disclosure in fork()",
+				"Description": "Usually this long long description of CVE-2019-1549",
+				"Severity": "MEDIUM",
+				"PrimaryURL": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549",
+				"References": [
+					"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549"
+				]
+			},
+			{
+				"VulnerabilityID": "CVE-2019-1547",
+				"PkgName": "openssl",
+				"InstalledVersion": "1.1.1c-r0",
+				"FixedVersion": "1.1.1d-r0",
+				"Title": "openssl: side-channel weak encryption vulnerability",
+				"Severity": "LOW",
+				"PrimaryURL": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
+				"References": [
+					"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547"
+				]
+			}
+		]
+	}]`
+
+	sampleReport = v1alpha1.VulnerabilityScanResult{
+		UpdateTimestamp: metav1.NewTime(fixedTime),
+		Scanner: v1alpha1.Scanner{
+			Name:    "Trivy",
+			Vendor:  "Aqua Security",
+			Version: "0.9.1",
+		},
+		Registry: v1alpha1.Registry{
+			Server: "index.docker.io",
+		},
+		Artifact: v1alpha1.Artifact{
+			Repository: "library/alpine",
+			Tag:        "3.10.2",
+		},
+		Summary: v1alpha1.VulnerabilitySummary{
+			CriticalCount: 0,
+			MediumCount:   1,
+			LowCount:      1,
+			NoneCount:     0,
+			UnknownCount:  0,
+		},
+		Vulnerabilities: []v1alpha1.Vulnerability{
+			{
+				VulnerabilityID:  "CVE-2019-1549",
+				Resource:         "openssl",
+				InstalledVersion: "1.1.1c-r0",
+				FixedVersion:     "1.1.1d-r0",
+				Severity:         v1alpha1.SeverityMedium,
+				Title:            "openssl: information disclosure in fork()",
+				PrimaryLink:      "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1549",
+				Links:            []string{},
+			},
+			{
+				VulnerabilityID:  "CVE-2019-1547",
+				Resource:         "openssl",
+				InstalledVersion: "1.1.1c-r0",
+				FixedVersion:     "1.1.1d-r0",
+				Severity:         v1alpha1.SeverityLow,
+				Title:            "openssl: side-channel weak encryption vulnerability",
+				PrimaryLink:      "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1547",
+				Links:            []string{},
+			},
+		},
+	}
+)
+
+func TestScanner_ParseVulnerabilityScanResult(t *testing.T) {
+	config := starboard.ConfigData{
+		"trivy.imageRef": "aquasec/trivy:0.9.1",
+	}
+
+	testCases := []struct {
+		name           string
+		imageRef       string
+		input          string
+		expectedError  error
+		expectedReport v1alpha1.VulnerabilityScanResult
+	}{
+		{
+			name:           "Should convert vulnerability report in JSON format when input is quiet",
+			imageRef:       "alpine:3.10.2",
+			input:          sampleReportAsString,
+			expectedError:  nil,
+			expectedReport: sampleReport,
+		},
+		{
+			name:          "Should convert vulnerability report in JSON format when OS is not detected",
+			imageRef:      "core.harbor.domain/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c",
+			input:         `null`,
+			expectedError: nil,
+			expectedReport: v1alpha1.VulnerabilityScanResult{
+				UpdateTimestamp: metav1.NewTime(fixedTime),
+				Scanner: v1alpha1.Scanner{
+					Name:    "Trivy",
+					Vendor:  "Aqua Security",
+					Version: "0.9.1",
+				},
+				Registry: v1alpha1.Registry{
+					Server: "core.harbor.domain",
+				},
+				Artifact: v1alpha1.Artifact{
+					Repository: "library/nginx",
+					Digest:     "sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c",
+				},
+				Summary: v1alpha1.VulnerabilitySummary{
+					CriticalCount: 0,
+					HighCount:     0,
+					MediumCount:   0,
+					LowCount:      0,
+					NoneCount:     0,
+					UnknownCount:  0,
+				},
+				Vulnerabilities: []v1alpha1.Vulnerability{},
+			},
+		},
+		{
+			name:          "Should return error when image reference cannot be parsed",
+			imageRef:      ":",
+			input:         "null",
+			expectedError: errors.New("could not parse reference: :"),
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			instance := trivy.NewPlugin(fixedClock, ext.NewSimpleIDGenerator(), config)
+			report, err := instance.ParseVulnerabilityScanResult(tc.imageRef, io.NopCloser(strings.NewReader(tc.input)))
+			switch {
+			case tc.expectedError == nil:
+				require.NoError(t, err)
+				assert.Equal(t, tc.expectedReport, report)
+			default:
+				assert.EqualError(t, err, tc.expectedError.Error())
+			}
+		})
+	}
+
+}
+
+func TestGetScoreFromCVSS(t *testing.T) {
+	testCases := []struct {
+		name          string
+		cvss          map[string]*trivy.CVSS
+		expectedScore *float64
+	}{
+		{
+			name: "Should return vendor score when vendor v3 score exist",
+			cvss: map[string]*trivy.CVSS{
+				"nvd": {
+					V3Score: pointer.Float64Ptr(8.1),
+				},
+				"redhat": {
+					V3Score: pointer.Float64Ptr(8.3),
+				},
+			},
+			expectedScore: pointer.Float64Ptr(8.3),
+		},
+		{
+			name: "Should return nvd score when vendor v3 score is nil",
+			cvss: map[string]*trivy.CVSS{
+				"nvd": {
+					V3Score: pointer.Float64Ptr(8.1),
+				},
+				"redhat": {
+					V3Score: nil,
+				},
+			},
+			expectedScore: pointer.Float64Ptr(8.1),
+		},
+		{
+			name: "Should return nvd score when vendor doesn't exist",
+			cvss: map[string]*trivy.CVSS{
+				"nvd": {
+					V3Score: pointer.Float64Ptr(8.1),
+				},
+			},
+			expectedScore: pointer.Float64Ptr(8.1),
+		},
+		{
+			name: "Should return nil when vendor and nvd both v3 scores are nil",
+			cvss: map[string]*trivy.CVSS{
+				"nvd": {
+					V3Score: nil,
+				},
+				"redhat": {
+					V3Score: nil,
+				},
+			},
+			expectedScore: nil,
+		},
+		{
+			name:          "Should return nil when cvss doesn't exist",
+			cvss:          map[string]*trivy.CVSS{},
+			expectedScore: nil,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			score := trivy.GetScoreFromCVSS(tc.cvss)
+			assert.Equal(t, tc.expectedScore, score)
+		})
+	}
+}
diff --git a/pkg/starboard/config_test.go b/pkg/starboard/config_test.go
index 0d6da00..07c5f8f 100644
--- a/pkg/starboard/config_test.go
+++ b/pkg/starboard/config_test.go
@@ -604,7 +604,7 @@ func TestConfigData_GetTrivyInsecureRegistries(t *testing.T) {
 }
 
 func TestGetScanJobTolerations(t *testing.T) {
-	testcases := []struct {
+	testCases := []struct {
 		name        string
 		config      starboard.ConfigData
 		expected    []corev1.Toleration
@@ -659,14 +659,16 @@ func TestGetScanJobTolerations(t *testing.T) {
 		},
 	}
 
-	for _, tc := range testcases {
-		got, err := tc.config.GetScanJobTolerations()
-		if tc.expectError != "" {
-			assert.Error(t, err, "unexpected end of JSON input", tc.name)
-		} else {
-			assert.NoError(t, err, tc.name)
-		}
-		assert.Equal(t, tc.expected, got, tc.name)
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, err := tc.config.GetScanJobTolerations()
+			if tc.expectError != "" {
+				assert.Error(t, err, "unexpected end of JSON input", tc.name)
+			} else {
+				assert.NoError(t, err, tc.name)
+			}
+			assert.Equal(t, tc.expected, got, tc.name)
+		})
 	}
 }
 
@@ -704,3 +706,56 @@ CVE-2019-1543`,
 		})
 	}
 }
+
+func TestGetScanJobAnnotations(t *testing.T) {
+	testCases := []struct {
+		name        string
+		config      starboard.ConfigData
+		expected    map[string]string
+		expectError string
+	}{
+		{
+			name: "scan job annotations can be fetched successfully",
+			config: starboard.ConfigData{
+				"scanJob.annotations": "a.b=c.d/e,foo=bar",
+			},
+			expected: map[string]string{
+				"foo": "bar",
+				"a.b": "c.d/e",
+			},
+		},
+		{
+			name:     "gracefully deal with unprovided annotations",
+			config:   starboard.ConfigData{},
+			expected: map[string]string{},
+		},
+		{
+			name: "raise an error on being provided with annotations in wrong format",
+			config: starboard.ConfigData{
+				"scanJob.annotations": "foo",
+			},
+			expected:    map[string]string{},
+			expectError: "custom annotations found to be wrongfully provided: foo",
+		},
+		{
+			name: "raise an error on being provided with annotations in wrong format",
+			config: starboard.ConfigData{
+				"scanJob.annotations": "foo=bar,a=b=c",
+			},
+			expected:    map[string]string{},
+			expectError: "custom annotations found to be wrongfully provided: foo=bar,a=b=c",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			scanJobAnnotations, err := tc.config.GetScanJobAnnotations()
+			if tc.expectError != "" {
+				assert.Error(t, err, tc.expectError, tc.name)
+			} else {
+				assert.NoError(t, err, tc.name)
+			}
+			assert.Equal(t, tc.expected, scanJobAnnotations, tc.name)
+		})
+	}
+}
diff --git a/pkg/vulnerabilityreport/scanner_test.go b/pkg/vulnerabilityreport/scanner_test.go
new file mode 100644
index 0000000..d36e439
--- /dev/null
+++ b/pkg/vulnerabilityreport/scanner_test.go
@@ -0,0 +1,1 @@
+package vulnerabilityreport_test