Repository: aquasecurity/starboard
Pull request #870 (2021-12-21 09:07:17)

feat(vulnrebilityreports) Only show the latest vulnrebilityreports from a deployment
Add environment variable: OPERATOR_VULNERABILITY_SCANNER_LATEST to be configured in the operator.
Setting this value to true will look through new replicasets and see if `deployment.kubernetes.io/revision` matches with it's deployment owner.
If that is true generate a report, else ignore it.

Solves:
https://github.com/aquasecurity/starboard/discussions/858
https://github.com/aquasecurity/starboard/discussions/668


CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/aquasecurity/starboard?pullRequest=870) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/aquasecurity/starboard?pullRequest=870) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/aquasecurity/starboard?pullRequest=870) it.</sub>
codecov[bot]: # [Codecov](https://codecov.io/gh/aquasecurity/starboard/pull/870?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) Report
> Merging [#870](https://codecov.io/gh/aquasecurity/starboard/pull/870?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (4e0d68f) into [main](https://codecov.io/gh/aquasecurity/starboard/commit/b8d85683bc4e347fb77c648218aba9cdbeb9fbf0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) (b8d8568) will **decrease** coverage by `19.72%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/aquasecurity/starboard/pull/870/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/870?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity)

```diff
@@             Coverage Diff             @@
##             main     #870       +/-   ##
===========================================
- Coverage   65.58%   45.86%   -19.73%     
===========================================
  Files          58       41       -17     
  Lines        6460     5296     -1164     
===========================================
- Hits         4237     2429     -1808     
- Misses       1796     2720      +924     
+ Partials      427      147      -280     
```


| [Impacted Files](https://codecov.io/gh/aquasecurity/starboard/pull/870?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity) | Coverage Δ | |
|---|---|---|
| [pkg/operator/controller/vulnerabilityreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-63.64%)` | :arrow_down: |
| [pkg/operator/etc/config.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity#diff-cGtnL29wZXJhdG9yL2V0Yy9jb25maWcuZ28=) | `75.75% <ø> (ø)` | |
| [pkg/plugin/factory.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-81.14%)` | :arrow_down: |
| [pkg/kubehunter/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-75.72%)` | :arrow_down: |
| [pkg/operator/controller/configauditreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-64.42%)` | :arrow_down: |
| [pkg/kube/runnable\_job.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-63.73%)` | :arrow_down: |
| [pkg/kubehunter/model.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-62.86%)` | :arrow_down: |
| [pkg/vulnerabilityreport/scanner.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-60.40%)` | :arrow_down: |
| [pkg/operator/controller/ciskubebenchreport.go](https://codecov.io/gh/aquasecurity/starboard/pull/870/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%> (-56.77%)` | :arrow_down: |
| ... and [32 more](https://codecov.io/gh/aquasecurity/starboard/pull/870/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/870?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/870?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aquasecurity). Last update [b8d8568...4e0d68f](https://codecov.io/gh/aquasecurity/starboard/pull/870?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).

NissesSenap: So I have applied the final fixes and I also clarified the documentation a bit. I have run it locally on my cluster using static yaml and it works as intended. 
Please have a look again @danielpacak 


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f134acb..a93ed76 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -69,7 +69,7 @@ These guidelines will help you get started with the Starboard project.
 ## Build Binaries
 
 | Binary                   | Image                                          | Description                                                   |
-| ------------------------ | ---------------------------------------------- |  ------------------------------------------------------------ |
+| ------------------------ | ---------------------------------------------- | ------------------------------------------------------------- |
 | `starboard`              | `docker.io/aquasec/starboard:dev`              | Starboard command-line interface                              |
 | `starboard-operator`     | `docker.io/aquasec/starboard-operator:dev`     | Starboard Operator                                            |
 | `starboard-scanner-aqua` | `docker.io/aquasec/starboard-scanner-aqua:dev` | Starboard plugin to integrate with Aqua vulnerability scanner |
@@ -237,6 +237,7 @@ basic development workflow. For other install modes see [Operator Multitenancy w
      OPERATOR_LOG_DEV_MODE=true \
      OPERATOR_CIS_KUBERNETES_BENCHMARK_ENABLED=true \
      OPERATOR_VULNERABILITY_SCANNER_ENABLED=true \
+     OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS=false \
      OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED=true \
      OPERATOR_BATCH_DELETE_LIMIT=3 \
      OPERATOR_BATCH_DELETE_DELAY="30s" \
diff --git a/deploy/helm/templates/deployment.yaml b/deploy/helm/templates/deployment.yaml
index 6234957..1df47bc 100644
--- a/deploy/helm/templates/deployment.yaml
+++ b/deploy/helm/templates/deployment.yaml
@@ -77,6 +77,8 @@ spec:
               value: {{ .Values.operator.vulnerabilityScannerEnabled | quote }}
             - name: OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED
               value: {{ .Values.operator.configAuditScannerEnabled | quote }}
+            - name: OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS
+              value: {{ .Values.operator.vulnerabilityScannerScanOnlyCurrentRevisions | quote }}
             {{- if gt (int .Values.operator.replicas) 1 }}
             - name: OPERATOR_LEADER_ELECTION_ENABLED
               value: "true"
diff --git a/deploy/helm/templates/rbac.yaml b/deploy/helm/templates/rbac.yaml
index 15c415f..6a03050 100644
--- a/deploy/helm/templates/rbac.yaml
+++ b/deploy/helm/templates/rbac.yaml
@@ -76,6 +76,7 @@ rules:
       - replicasets
       - statefulsets
       - daemonsets
+      - deployments
     verbs:
       - get
       - list
diff --git a/deploy/helm/values.yaml b/deploy/helm/values.yaml
index e1f925b..fc04c45 100644
--- a/deploy/helm/values.yaml
+++ b/deploy/helm/values.yaml
@@ -41,6 +41,8 @@ operator:
   kubernetesBenchmarkEnabled: true
   # batchDeleteLimit the maximum number of config audit reports deleted by the operator when the plugin's config has changed.
   batchDeleteLimit: 10
+  # vulnerabilityScannerScanOnlyCurrentRevisions the flag to only create vulnerability scans on the current revision of a deployment.
+  vulnerabilityScannerScanOnlyCurrentRevisions: false
   # batchDeleteDelay the duration to wait before deleting another batch of config audit reports.
   batchDeleteDelay: 10s
 image:
diff --git a/deploy/static/02-starboard-operator.rbac.yaml b/deploy/static/02-starboard-operator.rbac.yaml
index fe430e6..9af85bb 100644
--- a/deploy/static/02-starboard-operator.rbac.yaml
+++ b/deploy/static/02-starboard-operator.rbac.yaml
@@ -69,6 +69,7 @@ rules:
       - replicasets
       - statefulsets
       - daemonsets
+      - deployments
     verbs:
       - get
       - list
diff --git a/deploy/static/04-starboard-operator.deployment.yaml b/deploy/static/04-starboard-operator.deployment.yaml
index 485db1b..c87fa9f 100644
--- a/deploy/static/04-starboard-operator.deployment.yaml
+++ b/deploy/static/04-starboard-operator.deployment.yaml
@@ -81,6 +81,8 @@ spec:
               value: "true"
             - name: OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED
               value: "true"
+            - name: OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS
+              value: "false"
           ports:
             - name: metrics
               containerPort: 8080
diff --git a/docs/operator/configuration.md b/docs/operator/configuration.md
index b7d73ca..c82e034 100644
--- a/docs/operator/configuration.md
+++ b/docs/operator/configuration.md
@@ -1,34 +1,35 @@
 Configuration of the operator's Pod is done via environment variables at startup.
 
-| NAME                                        | DEFAULT                | DESCRIPTION |
-| ------------------------------------------- | ---------------------- | ----------- |
-| `OPERATOR_NAMESPACE`                        | N/A                    | See [Install modes](#install-modes) |
-| `OPERATOR_TARGET_NAMESPACES`                | N/A                    | See [Install modes](#install-modes) |
-| `OPERATOR_SERVICE_ACCOUNT`                  | `starboard-operator`   | The name of the service account assigned to the operator's pod |
-| `OPERATOR_LOG_DEV_MODE`                     | `false`                | The flag to use (or not use) development mode (more human-readable output, extra stack traces and logging information, etc). |
-| `OPERATOR_SCAN_JOB_TIMEOUT`                 | `5m`                   | The length of time to wait before giving up on a scan job |
-| `OPERATOR_CONCURRENT_SCAN_JOBS_LIMIT`       | `10`                   | The maximum number of scan jobs create by the operator |
-| `OPERATOR_SCAN_JOB_RETRY_AFTER`             | `30s`                  | The duration to wait before retrying a failed scan job |
-| `OPERATOR_BATCH_DELETE_LIMIT`               | `10`                   | The maximum number of config audit reports deleted by the operator when the plugin's config has changed. |
-| `OPERATOR_BATCH_DELETE_DELAY`               | `10s`                  | The duration to wait before deleting another batch of config audit reports. |
-| `OPERATOR_METRICS_BIND_ADDRESS`             | `:8080`                | The TCP address to bind to for serving [Prometheus][prometheus] metrics. It can be set to `0` to disable the metrics serving. |
-| `OPERATOR_HEALTH_PROBE_BIND_ADDRESS`        | `:9090`                | The TCP address to bind to for serving health probes, i.e. `/healthz/` and `/readyz/` endpoints. |
-| `OPERATOR_CIS_KUBERNETES_BENCHMARK_ENABLED` | `true`                 | The flag to enable CIS Kubernetes Benchmark scanner |
-| `OPERATOR_VULNERABILITY_SCANNER_ENABLED`    | `true`                 | The flag to enable vulnerability scanner |
-| `OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED`     | `true`                 | The flag to enable configuration audit scanner |
-| `OPERATOR_LEADER_ELECTION_ENABLED`          | `false`                | The flag to enable operator replica leader election |
-| `OPERATOR_LEADER_ELECTION_ID`               | `starboard-lock`       | The name of the resource lock for leader election |
+| NAME                                                         | DEFAULT              | DESCRIPTION                                                                                                                   |
+| ------------------------------------------------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
+| `OPERATOR_NAMESPACE`                                         | N/A                  | See [Install modes](#install-modes)                                                                                           |
+| `OPERATOR_TARGET_NAMESPACES`                                 | N/A                  | See [Install modes](#install-modes)                                                                                           |
+| `OPERATOR_SERVICE_ACCOUNT`                                   | `starboard-operator` | The name of the service account assigned to the operator's pod                                                                |
+| `OPERATOR_LOG_DEV_MODE`                                      | `false`              | The flag to use (or not use) development mode (more human-readable output, extra stack traces and logging information, etc).  |
+| `OPERATOR_SCAN_JOB_TIMEOUT`                                  | `5m`                 | The length of time to wait before giving up on a scan job                                                                     |
+| `OPERATOR_CONCURRENT_SCAN_JOBS_LIMIT`                        | `10`                 | The maximum number of scan jobs create by the operator                                                                        |
+| `OPERATOR_SCAN_JOB_RETRY_AFTER`                              | `30s`                | The duration to wait before retrying a failed scan job                                                                        |
+| `OPERATOR_BATCH_DELETE_LIMIT`                                | `10`                 | The maximum number of config audit reports deleted by the operator when the plugin's config has changed.                      |
+| `OPERATOR_BATCH_DELETE_DELAY`                                | `10s`                | The duration to wait before deleting another batch of config audit reports.                                                   |
+| `OPERATOR_METRICS_BIND_ADDRESS`                              | `:8080`              | The TCP address to bind to for serving [Prometheus][prometheus] metrics. It can be set to `0` to disable the metrics serving. |
+| `OPERATOR_HEALTH_PROBE_BIND_ADDRESS`                         | `:9090`              | The TCP address to bind to for serving health probes, i.e. `/healthz/` and `/readyz/` endpoints.                              |
+| `OPERATOR_CIS_KUBERNETES_BENCHMARK_ENABLED`                  | `true`               | The flag to enable CIS Kubernetes Benchmark scanner                                                                           |
+| `OPERATOR_VULNERABILITY_SCANNER_ENABLED`                     | `true`               | The flag to enable vulnerability scanner                                                                                      |
+| `OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED`                      | `true`               | The flag to enable configuration audit scanner                                                                                |
+| `OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS` | `false`              | The flag to enable vulnerability scanner to only scan the current revision of a deployment                                    |
+| `OPERATOR_LEADER_ELECTION_ENABLED`                           | `false`              | The flag to enable operator replica leader election                                                                           |
+| `OPERATOR_LEADER_ELECTION_ID`                                | `starboard-lock`     | The name of the resource lock for leader election                                                                             |
 
 ## Install Modes
 
 The values of the `OPERATOR_NAMESPACE` and `OPERATOR_TARGET_NAMESPACES` determine
 the install mode, which in turn determines the multitenancy support of the operator.
 
-| MODE            | OPERATOR_NAMESPACE | OPERATOR_TARGET_NAMESPACES | DESCRIPTION |
-| --------------- | ------------------ | -------------------------- | ----------- |
-| OwnNamespace    | `operators`        | `operators`                | The operator can be configured to watch events in the namespace it is deployed in. |
+| MODE            | OPERATOR_NAMESPACE | OPERATOR_TARGET_NAMESPACES | DESCRIPTION                                                                                                    |
+| --------------- | ------------------ | -------------------------- | -------------------------------------------------------------------------------------------------------------- |
+| OwnNamespace    | `operators`        | `operators`                | The operator can be configured to watch events in the namespace it is deployed in.                             |
 | SingleNamespace | `operators`        | `foo`                      | The operator can be configured to watch for events in a single namespace that the operator is not deployed in. |
-| MultiNamespace  | `operators`        | `foo,bar,baz`              | The operator can be configured to watch for events in more than one namespace. |
-| AllNamespaces   | `operators`        | (blank string)             | The operator can be configured to watch for events in all namespaces. |
+| MultiNamespace  | `operators`        | `foo,bar,baz`              | The operator can be configured to watch for events in more than one namespace.                                 |
+| AllNamespaces   | `operators`        | (blank string)             | The operator can be configured to watch for events in all namespaces.                                          |
 
 [prometheus]: https://github.com/prometheus
diff --git a/docs/operator/getting-started.md b/docs/operator/getting-started.md
index b8a33c2..fadf401 100644
--- a/docs/operator/getting-started.md
+++ b/docs/operator/getting-started.md
@@ -98,6 +98,9 @@ collection. For example, when the previous ReplicaSet named `nginx-6d4cf56db6` i
 `replicaset-nginx-6d4cf56db6-nginx` as well as the ConfigAuditReport named `replicaset-nginx-6d4cf56db6` are
 automatically garbage collected.
 
+If you only want the latest replicaset in your deployment to be scanned for vulnerabilities you can define `OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS=true`
+in your operator deployment. This can be useful if you only want to know about vulnerability that is currently a potential issue.
+
 !!! tip
     You can get and describe `vulnerabilityreports` and `configauditreports` as built-in Kubernetes objects:
     ```
diff --git a/pkg/kube/object.go b/pkg/kube/object.go
index 3c8d351..7741b3e 100644
--- a/pkg/kube/object.go
+++ b/pkg/kube/object.go
@@ -57,9 +57,10 @@ const (
 	KindRole                  Kind = "Role"
 	KindRoleBinding           Kind = "RoleBinding"
 
-	KindClusterRole              Kind = "ClusterRole"
-	KindClusterRoleBindings      Kind = "ClusterRoleBinding"
-	KindCustomResourceDefinition Kind = "CustomResourceDefinition"
+	KindClusterRole              Kind   = "ClusterRole"
+	KindClusterRoleBindings      Kind   = "ClusterRoleBinding"
+	KindCustomResourceDefinition Kind   = "CustomResourceDefinition"
+	deploymentAnnotation         string = "deployment.kubernetes.io/revision"
 )
 
 // IsBuiltInWorkload returns true if the specified v1.OwnerReference
@@ -488,3 +489,21 @@ func (o *ObjectResolver) getReplicaSetByPod(ctx context.Context, object Object)
 	}
 	return controller.Name, nil
 }
+
+func (o *ObjectResolver) IsActiveReplicaSet(ctx context.Context, workloadObj client.Object, controller *metav1.OwnerReference) (bool, error) {
+	if controller != nil && controller.Kind == string(KindDeployment) {
+		deploymentObject := &appsv1.Deployment{}
+
+		err := o.Client.Get(ctx, client.ObjectKey{
+			Namespace: workloadObj.GetNamespace(),
+			Name:      controller.Name,
+		}, deploymentObject)
+		if err != nil {
+			return false, err
+		}
+		deploymentRevisionAnnotation := deploymentObject.GetAnnotations()
+		replicasetRevisionAnnotation := workloadObj.GetAnnotations()
+		return replicasetRevisionAnnotation[deploymentAnnotation] == deploymentRevisionAnnotation[deploymentAnnotation], nil
+	}
+	return true, nil
+}
diff --git a/pkg/operator/controller/vulnerabilityreport.go b/pkg/operator/controller/vulnerabilityreport.go
index 95ee0d6..1ec65a0 100644
--- a/pkg/operator/controller/vulnerabilityreport.go
+++ b/pkg/operator/controller/vulnerabilityreport.go
@@ -108,6 +108,18 @@ func (r *VulnerabilityReportReconciler) reconcileWorkload(workloadKind kube.Kind
 			}
 		}
 
+		if r.Config.VulnerabilityScannerScanOnlyCurrentRevisions && workloadKind == kube.KindReplicaSet {
+			controller := metav1.GetControllerOf(workloadObj)
+			activeReplicaSet, err := r.IsActiveReplicaSet(ctx, workloadObj, controller)
+			if err != nil {
+				return ctrl.Result{}, fmt.Errorf("failed checking current revision: %w", err)
+			}
+			if !activeReplicaSet {
+				log.V(1).Info("Ignoring inactive ReplicaSet", "controllerKind", controller.Kind, "controllerName", controller.Name)
+				return ctrl.Result{}, nil
+			}
+		}
+
 		// Skip processing if it's a Job controlled by CronJob.
 		if workloadKind == kube.KindJob {
 			controller := metav1.GetControllerOf(workloadObj)
diff --git a/pkg/operator/etc/config.go b/pkg/operator/etc/config.go
index 7ae86d4..77f32f6 100644
--- a/pkg/operator/etc/config.go
+++ b/pkg/operator/etc/config.go
@@ -10,22 +10,23 @@ import (
 
 // Config defines parameters for running the operator.
 type Config struct {
-	Namespace                     string        `env:"OPERATOR_NAMESPACE"`
-	TargetNamespaces              string        `env:"OPERATOR_TARGET_NAMESPACES"`
-	ServiceAccount                string        `env:"OPERATOR_SERVICE_ACCOUNT" envDefault:"starboard-operator"`
-	LogDevMode                    bool          `env:"OPERATOR_LOG_DEV_MODE" envDefault:"false"`
-	ScanJobTimeout                time.Duration `env:"OPERATOR_SCAN_JOB_TIMEOUT" envDefault:"5m"`
-	ConcurrentScanJobsLimit       int           `env:"OPERATOR_CONCURRENT_SCAN_JOBS_LIMIT" envDefault:"10"`
-	ScanJobRetryAfter             time.Duration `env:"OPERATOR_SCAN_JOB_RETRY_AFTER" envDefault:"30s"`
-	BatchDeleteLimit              int           `env:"OPERATOR_BATCH_DELETE_LIMIT" envDefault:"10"`
-	BatchDeleteDelay              time.Duration `env:"OPERATOR_BATCH_DELETE_DELAY" envDefault:"10s"`
-	MetricsBindAddress            string        `env:"OPERATOR_METRICS_BIND_ADDRESS" envDefault:":8080"`
-	HealthProbeBindAddress        string        `env:"OPERATOR_HEALTH_PROBE_BIND_ADDRESS" envDefault:":9090"`
-	CISKubernetesBenchmarkEnabled bool          `env:"OPERATOR_CIS_KUBERNETES_BENCHMARK_ENABLED" envDefault:"true"`
-	VulnerabilityScannerEnabled   bool          `env:"OPERATOR_VULNERABILITY_SCANNER_ENABLED" envDefault:"true"`
-	ConfigAuditScannerEnabled     bool          `env:"OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED" envDefault:"true"`
-	LeaderElectionEnabled         bool          `env:"OPERATOR_LEADER_ELECTION_ENABLED" envDefault:"false"`
-	LeaderElectionID              string        `env:"OPERATOR_LEADER_ELECTION_ID" envDefault:"starboard-lock"`
+	Namespace                                    string        `env:"OPERATOR_NAMESPACE"`
+	TargetNamespaces                             string        `env:"OPERATOR_TARGET_NAMESPACES"`
+	ServiceAccount                               string        `env:"OPERATOR_SERVICE_ACCOUNT" envDefault:"starboard-operator"`
+	LogDevMode                                   bool          `env:"OPERATOR_LOG_DEV_MODE" envDefault:"false"`
+	ScanJobTimeout                               time.Duration `env:"OPERATOR_SCAN_JOB_TIMEOUT" envDefault:"5m"`
+	ConcurrentScanJobsLimit                      int           `env:"OPERATOR_CONCURRENT_SCAN_JOBS_LIMIT" envDefault:"10"`
+	ScanJobRetryAfter                            time.Duration `env:"OPERATOR_SCAN_JOB_RETRY_AFTER" envDefault:"30s"`
+	BatchDeleteLimit                             int           `env:"OPERATOR_BATCH_DELETE_LIMIT" envDefault:"10"`
+	BatchDeleteDelay                             time.Duration `env:"OPERATOR_BATCH_DELETE_DELAY" envDefault:"10s"`
+	MetricsBindAddress                           string        `env:"OPERATOR_METRICS_BIND_ADDRESS" envDefault:":8080"`
+	HealthProbeBindAddress                       string        `env:"OPERATOR_HEALTH_PROBE_BIND_ADDRESS" envDefault:":9090"`
+	CISKubernetesBenchmarkEnabled                bool          `env:"OPERATOR_CIS_KUBERNETES_BENCHMARK_ENABLED" envDefault:"true"`
+	VulnerabilityScannerEnabled                  bool          `env:"OPERATOR_VULNERABILITY_SCANNER_ENABLED" envDefault:"true"`
+	ConfigAuditScannerEnabled                    bool          `env:"OPERATOR_CONFIG_AUDIT_SCANNER_ENABLED" envDefault:"true"`
+	LeaderElectionEnabled                        bool          `env:"OPERATOR_LEADER_ELECTION_ENABLED" envDefault:"false"`
+	LeaderElectionID                             string        `env:"OPERATOR_LEADER_ELECTION_ID" envDefault:"starboard-lock"`
+	VulnerabilityScannerScanOnlyCurrentRevisions bool          `env:"OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS" envDefault:"false"`
 }
 
 // GetOperatorConfig loads Config from environment variables.


Test patch:
diff --git a/pkg/kube/object_test.go b/pkg/kube/object_test.go
index 8053679..12310a7 100644
--- a/pkg/kube/object_test.go
+++ b/pkg/kube/object_test.go
@@ -957,3 +957,184 @@ func TestObjectResolver_ReportOwner(t *testing.T) {
 		})
 	}
 }
+
+func TestObjectResolver_IsActiveReplicaSet(t *testing.T) {
+	nginxDeploy := &appsv1.Deployment{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "apps/v1",
+			Kind:       "Deployment",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: corev1.NamespaceDefault,
+			Name:      "nginx",
+			Labels: map[string]string{
+				"app": "nginx",
+			},
+			Annotations: map[string]string{
+				"deployment.kubernetes.io/revision": "1",
+			},
+			UID: "734c1370-2281-4946-9b5f-940b33f3e4b8",
+		},
+		Spec: appsv1.DeploymentSpec{
+			Replicas: pointer.Int32Ptr(1),
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"app": "nginx",
+				},
+			},
+			Template: corev1.PodTemplateSpec{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: corev1.NamespaceDefault,
+					Name:      "nginx",
+					Labels: map[string]string{
+						"app": "nginx",
+					},
+					Annotations: map[string]string{
+						"deployment.kubernetes.io/revision": "1",
+					},
+				},
+			},
+		},
+	}
+	nginxReplicaSet := &appsv1.ReplicaSet{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "apps/v1",
+			Kind:       "ReplicaSet",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: corev1.NamespaceDefault,
+			Name:      "nginx-6d4cf56db6",
+			Labels: map[string]string{
+				"app":               "nginx",
+				"pod-template-hash": "6d4cf56db6",
+			},
+			Annotations: map[string]string{
+				"deployment.kubernetes.io/desired-replicas": "1",
+				"deployment.kubernetes.io/max-replicas":     "4",
+				"deployment.kubernetes.io/revision":         "1",
+			},
+			UID: "ecfff877-784c-4f05-8b70-abe441ca1976",
+			OwnerReferences: []metav1.OwnerReference{
+				{
+					APIVersion:         "apps/v1",
+					Kind:               "Deployment",
+					Name:               "nginx",
+					UID:                "734c1370-2281-4946-9b5f-940b33f3e4b8",
+					Controller:         pointer.BoolPtr(true),
+					BlockOwnerDeletion: pointer.BoolPtr(true),
+				},
+			},
+		},
+		Spec: appsv1.ReplicaSetSpec{
+			Replicas: pointer.Int32(1),
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"app":               "nginx",
+					"pod-template-hash": "6d4cf56db6",
+				},
+			},
+		},
+	}
+	notActiveNginxReplicaSet := &appsv1.ReplicaSet{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "apps/v1",
+			Kind:       "ReplicaSet",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: corev1.NamespaceDefault,
+			Name:      "nginx-f88799b98",
+			Labels: map[string]string{
+				"app":               "nginx",
+				"pod-template-hash": "f88799b98",
+			},
+			Annotations: map[string]string{
+				"deployment.kubernetes.io/desired-replicas": "1",
+				"deployment.kubernetes.io/max-replicas":     "4",
+				"deployment.kubernetes.io/revision":         "2",
+			},
+			UID: "6fd87db4-d557-4b84-92b7-653c3f4e5c7d",
+			OwnerReferences: []metav1.OwnerReference{
+				{
+					APIVersion:         "apps/v1",
+					Kind:               "Deployment",
+					Name:               "nginx",
+					UID:                "734c1370-2281-4946-9b5f-940b33f3e4b8",
+					Controller:         pointer.BoolPtr(true),
+					BlockOwnerDeletion: pointer.BoolPtr(true),
+				},
+			},
+		},
+		Spec: appsv1.ReplicaSetSpec{
+			Replicas: pointer.Int32(1),
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"app":               "nginx",
+					"pod-template-hash": "f88799b98",
+				},
+			},
+		},
+	}
+	standAloneNginxReplicaSet := &appsv1.ReplicaSet{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "apps/v1",
+			Kind:       "ReplicaSet",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: corev1.NamespaceDefault,
+			Name:      "nginx-d54df7dc7",
+			Labels: map[string]string{
+				"app":               "nginx",
+				"pod-template-hash": "d54df7dc7",
+			},
+			Annotations: map[string]string{
+				"deployment.kubernetes.io/desired-replicas": "1",
+				"deployment.kubernetes.io/max-replicas":     "4",
+			},
+			UID: "0eed5ccf-4518-4ae7-933e-cafded6cf356",
+		},
+		Spec: appsv1.ReplicaSetSpec{
+			Replicas: pointer.Int32(1),
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"app":               "nginx",
+					"pod-template-hash": "d54df7dc7",
+				},
+			},
+		},
+	}
+	testClient := fake.NewClientBuilder().WithScheme(starboard.NewScheme()).WithObjects(
+		nginxDeploy,
+		nginxReplicaSet,
+		notActiveNginxReplicaSet,
+	).Build()
+	testCases := []struct {
+		name     string
+		resource *appsv1.ReplicaSet
+		result   bool
+	}{
+		{
+			name:     "activeReplicaset",
+			resource: nginxReplicaSet,
+			result:   true,
+		},
+		{
+			name:     "noneActiveReplicaset",
+			resource: notActiveNginxReplicaSet,
+			result:   false,
+		},
+		{
+			name:     "standAloneReplicaset",
+			resource: standAloneNginxReplicaSet,
+			result:   true,
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			or := kube.ObjectResolver{Client: testClient}
+			controller := metav1.GetControllerOf(tc.resource)
+			isActive, err := or.IsActiveReplicaSet(context.TODO(), tc.resource, controller)
+			require.NoError(t, err)
+			assert.Equal(t, isActive, tc.result)
+		})
+	}
+}