Repository: FairwindsOps/polaris
Pull request #820 (2022-08-09 22:15:09)

FWI-2547: Add checks for RBAC allowing execing or attaching to a Pod
## Checklist
* [x] I have signed the CLA
* [x] I have updated/added any relevant documentation

## Description
### What's the goal of this PR?
* Alert on user-defined Roles and ClusterRoles that allow execing or attaching to pods.
* Alert on user-defined RoleBindings and ClusterRoleBindings that reference Roles or ClusterRoles allowing execing or attaching to pods.

### What alternative solution should we consider, if any?

The ClusterRole check factors out static names of built-in ClusterRoles that allow exec or attach. I chose not to match partial ClusterRole names like `system:*` or standard labels, because it is possible for user-defined ClusterRoles to be created with these same criteria. If future versions of Kubernetes add ClusterRoles with these permissions, those new ClusterRoles will need to be added to this check.

fairwinds-insights[bot]: ## Fairwinds Insights CI Report
> [View the Full Report](https://insights.fairwinds.com/gh/FairwindsOps/polaris/FWI-2547)

:white_check_mark: No new Action Items detected!
ivanfetch-fw: I've added a new schema test which will fail until [Polaris PR 822](https://github.com/FairwindsOps/polaris/pull/822) is merged - fixing an issue with namespace filtering Kube resources for additional schema validation.
ivanfetch-fw: @makoscafee  Thank you for the review. Let me know whether you have additional things you think I should address, or whether this is good to merge.


> LGTM just wanted to ask if the test for ClusterRole/ClusterRoleBinding/Role/RoleBinding which has these constants in their names was not included on purpose
> -"cluster-admin"
> - "system:controller:generic-garbage-collector"
> - "system:controller:namespace-controller"
> etc.
> ```

I did add a couple more tests for those, thanks for that suggestion.

I also removed code that checks for default bindings to ClusterRoles and Roles, because there actually are not any default bindings in Kube - that code being in those 2 checks was an artifact of copy/pasting from the ClusterRoleBinding check.

Patch:
diff --git a/checks/clusterrolePodExecAttach.yaml b/checks/clusterrolePodExecAttach.yaml
new file mode 100644
index 00000000..efc7bdc2
--- /dev/null
+++ b/checks/clusterrolePodExecAttach.yaml
@@ -0,0 +1,55 @@
+successMessage: The ClusterRole does not allow pods/exec or pods/attach
+failureMessage: The ClusterRole allows Pods/exec or pods/attach
+category: Security
+target: rbac.authorization.k8s.io/ClusterRole
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  required: ["metadata", "rules"]
+  anyOf:
+    # Do not alert on default ClusterRoles.
+    - properties:
+        metadata:
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - const: 'admin'
+                - const: "cluster-admin"
+                - const: "edit"
+                - const: "system:aggregate-to-edit"
+                - const: "system:controller:generic-garbage-collector"
+                - const: "system:controller:namespace-controller"
+    - properties:
+        rules:
+          type: array
+          items:
+            type: object
+            not:
+              required: ["apiGroups", "resources", "verbs"]
+              properties:
+                apiGroups:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: ""
+                      - const: '*'
+                resources:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: '*'
+                      - const: "pods/exec"
+                      - const: "pods/attach"
+                verbs:
+                  type: array
+                  contains:
+                    type: string
+                    anyOf:
+                      - const: '*'
+                      # An exec is also possible by `get`ing a web socket.
+                      - const: 'get'
+                      - const: 'create'
diff --git a/checks/clusterrolebindingPodExecAttach.yaml b/checks/clusterrolebindingPodExecAttach.yaml
new file mode 100644
index 00000000..f1a4a6d4
--- /dev/null
+++ b/checks/clusterrolebindingPodExecAttach.yaml
@@ -0,0 +1,82 @@
+successMessage: The ClusterRoleBinding does not reference a ClusterRole allowing pods/exec or pods/attach
+failureMessage: The ClusterRoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist
+category: Security
+target: rbac.authorization.k8s.io/ClusterRoleBinding
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  anyOf:
+    # Do not alert on default ClusterRoleBindings.
+    - required: ["metadata"]
+      properties:
+        metadata:
+          type: object
+          required: ["name"]
+          properties:
+            name:
+              type: string
+              anyOf:
+                - const: "cluster-admin"
+                - const: "system:controller:generic-garbage-collector"
+                - const: "system:controller:namespace-controller"
+    - required: ["roleRef"]
+      properties:
+        roleRef:
+          required: ["apiGroup", "kind", "name"]
+          properties:
+            apiGroup:
+              type: string
+              const: "rbac.authorization.k8s.io"
+            kind:
+              type: string
+              const: "ClusterRole"
+            name:
+              type: string
+              minLength: 1
+additionalSchemaStrings:
+  rbac.authorization.k8s.io/ClusterRole: |
+    type: object
+    # Do not alert on default ClusterRoleBindings.
+    {{ if and (ne .metadata.name "cluster-admin") (ne .metadata.name "system:controller:generic-garbage-collector") (ne .metadata.name "system:controller:namespace-controller") }}
+    required: ["metadata", "rules"]
+    allOf:
+      - properties:
+          metadata:
+            required: ["name"]
+            properties:
+              name:
+                type: string
+                const: "{{ .roleRef.name }}"
+      - properties:
+          rules:
+            type: array
+            items:
+              type: object
+              not:
+                required: ["apiGroups", "resources", "verbs"]
+                properties:
+                  apiGroups:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: ""
+                        - const: '*'
+                  resources:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        - const: "pods/exec"
+                        - const: "pods/attach"
+                  verbs:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        # An exec is also possible by `get`ing a web socket.
+                        - const: 'get'
+                        - const: 'create'
+    {{ end }}
diff --git a/checks/rolePodExecAttach.yaml b/checks/rolePodExecAttach.yaml
new file mode 100644
index 00000000..61c27021
--- /dev/null
+++ b/checks/rolePodExecAttach.yaml
@@ -0,0 +1,45 @@
+successMessage: The Role does not allow pods/exec or pods/attach
+failureMessage: The Role allows Pods/exec or pods/attach
+category: Security
+target: rbac.authorization.k8s.io/Role
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  required: ["metadata", "rules"]
+  properties:
+    metadata:
+      required: ["name"]
+      properties:
+        name:
+          type: string
+    rules:
+      type: array
+      items:
+        type: object
+        not:
+          required: ["apiGroups", "resources", "verbs"]
+          properties:
+            apiGroups:
+              type: array
+              contains:
+                type: string
+                anyOf:
+                  - const: ""
+                  - const: '*'
+            resources:
+              type: array
+              contains:
+                type: string
+                anyOf:
+                  - const: '*'
+                  - const: "pods/exec"
+                  - const: "pods/attach"
+            verbs:
+              type: array
+              contains:
+                type: string
+                anyOf:
+                  - const: '*'
+                  # An exec is also possible by `get`ing a web socket.
+                  - const: 'get'
+                  - const: 'create'
diff --git a/checks/rolebindingClusterRolePodExecAttach.yaml b/checks/rolebindingClusterRolePodExecAttach.yaml
new file mode 100644
index 00000000..87e1c02e
--- /dev/null
+++ b/checks/rolebindingClusterRolePodExecAttach.yaml
@@ -0,0 +1,78 @@
+successMessage: The RoleBinding does not reference a ClusterRole allowing pods/exec or pods/attach
+failureMessage: The RoleBinding references a ClusterRole that allows Pods/exec, allows pods/attach, or that does not exist
+category: Security
+target: rbac.authorization.k8s.io/RoleBinding
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  anyOf:
+    # Pass RoleBindings that point to a Role.
+    - required: ["roleRef"]
+      properties:
+        roleRef:
+          required: ["kind"]
+          properties:
+            kind:
+              type: string
+              const: "Role"
+    - required: ["roleRef"]
+      properties:
+        roleRef:
+          required: ["apiGroup", "kind", "name"]
+          properties:
+            apiGroup:
+              type: string
+              const: "rbac.authorization.k8s.io"
+            kind:
+              type: string
+              const: "ClusterRole"
+            name:
+              type: string
+              minLength: 1
+additionalSchemaStrings:
+  rbac.authorization.k8s.io/ClusterRole: |
+    type: object
+    # This schema is validated for all roleBindings, regardless of their roleRef.
+    {{ if eq .roleRef.kind "ClusterRole" }}
+    required: ["metadata", "rules"]
+    allOf:
+      - properties:
+          metadata:
+            required: ["name"]
+            properties:
+              name:
+                type: string
+                const: "{{ .roleRef.name }}"
+      - properties:
+          rules:
+            type: array
+            items:
+              type: object
+              not:
+                required: ["apiGroups", "resources", "verbs"]
+                properties:
+                  apiGroups:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: ""
+                        - const: '*'
+                  resources:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        - const: "pods/exec"
+                        - const: "pods/attach"
+                  verbs:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        # An exec is also possible by `get`ing a web socket.
+                        - const: 'get'
+                        - const: 'create'
+    {{ end }}
diff --git a/checks/rolebindingRolePodExecAttach.yaml b/checks/rolebindingRolePodExecAttach.yaml
new file mode 100644
index 00000000..06fb8287
--- /dev/null
+++ b/checks/rolebindingRolePodExecAttach.yaml
@@ -0,0 +1,81 @@
+successMessage: The RoleBinding does not reference a Role allowing Pod exec or attach
+failureMessage: The RoleBinding references a Role that allows Pods/exec, allows pods/attach, or that does not exist
+category: Security
+target: rbac.authorization.k8s.io/RoleBinding
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  anyOf:
+    # Pass RoleBindings that point to a ClusterRole.
+    - required: ["roleRef"]
+      properties:
+        roleRef:
+          required: ["apiGroup", "kind", "name"]
+          properties:
+            apiGroup:
+              type: string
+              const: "rbac.authorization.k8s.io"
+            kind:
+              type: string
+              const: "Role"
+    - required: ["roleRef"]
+      properties:
+        roleRef:
+          required: ["apiGroup", "kind", "name"]
+          properties:
+            apiGroup:
+              type: string
+              const: "rbac.authorization.k8s.io"
+            kind:
+              type: string
+              const: "Role"
+            name:
+              type: string
+              minLength: 1
+additionalSchemaStrings:
+  rbac.authorization.k8s.io/Role: |
+    type: object
+    # This schema is validated for all roleBindings, regardless of their roleRef.
+    {{ if eq .roleRef.kind "Role" }}
+    required: ["metadata", "rules"]
+    allOf:
+      - properties:
+          metadata:
+            required: ["name"]
+            properties:
+              name:
+                type: string
+                const: "{{ .roleRef.name }}"
+      - properties:
+          rules:
+            type: array
+            items:
+              type: object
+              not:
+                required: ["apiGroups", "resources", "verbs"]
+                properties:
+                  apiGroups:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: ""
+                        - const: '*'
+                  resources:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        - const: "pods/exec"
+                        - const: "pods/attach"
+                  verbs:
+                    type: array
+                    contains:
+                      type: string
+                      anyOf:
+                        - const: '*'
+                        # An exec is also possible by `get`ing a web socket.
+                        - const: 'get'
+                        - const: 'create'
+    {{ end }}
diff --git a/examples/config-full.yaml b/examples/config-full.yaml
index 23c6c93e..15804c58 100644
--- a/examples/config-full.yaml
+++ b/examples/config-full.yaml
@@ -27,6 +27,11 @@ checks:
   hostPortSet: warning
   sensitiveContainerEnvVar: danger
   sensitiveConfigmapContent: danger
+  clusterrolePodExecAttach: danger
+  rolePodExecAttach: danger
+  clusterrolebindingPodExecAttach: danger
+  rolebindingClusterRolePodExecAttach: danger
+  rolebindingRolePodExecAttach: danger
   # custom
   resourceLimits: warning
   imageRegistry: danger
diff --git a/pkg/config/checks.go b/pkg/config/checks.go
index 8f26bca4..f15a2e8b 100644
--- a/pkg/config/checks.go
+++ b/pkg/config/checks.go
@@ -59,6 +59,11 @@ var (
 		"missingPodDisruptionBudget",
 		"missingNetworkPolicy",
 		"sensitiveConfigmapContent",
+		"clusterrolePodExecAttach",
+		"rolePodExecAttach",
+		"clusterrolebindingPodExecAttach",
+		"rolebindingClusterRolePodExecAttach",
+		"rolebindingRolePodExecAttach",
 		"clusterrolebindingClusterAdmin",
 		"rolebindingClusterAdminClusterRole",
 		"rolebindingClusterAdminRole",


Test patch:
diff --git a/test/checks/clusterrolePodExecAttach/failure.all_wildcards.yaml b/test/checks/clusterrolePodExecAttach/failure.all_wildcards.yaml
new file mode 100644
index 00000000..09a04e59
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.all_wildcards.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole apiGroups, resources, and verbs are all * which allows pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.attach_explicit.yaml b/test/checks/clusterrolePodExecAttach/failure.attach_explicit.yaml
new file mode 100644
index 00000000..61c69eca
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.attach_explicit.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.attach_using_verb_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.attach_using_verb_wildcard.yaml
new file mode 100644
index 00000000..3de50393
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.attach_using_verb_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "*" ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..ba26bae4
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ create ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..fd0ccc82
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..8106791b
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.exec_explicit.yaml b/test/checks/clusterrolePodExecAttach/failure.exec_explicit.yaml
new file mode 100644
index 00000000..2f5dd88f
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.exec_explicit.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/exec.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.exec_verb_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.exec_verb_wildcard.yaml
new file mode 100644
index 00000000..6c041780
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.exec_verb_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/exec using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "*" ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..676b83d3
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ get ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..908bbdf6
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ get ]
diff --git a/test/checks/clusterrolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml b/test/checks/clusterrolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..a5da2b58
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,9 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ get ]
diff --git a/test/checks/clusterrolePodExecAttach/success.allow_unrelated_perms.yaml b/test/checks/clusterrolePodExecAttach/success.allow_unrelated_perms.yaml
new file mode 100644
index 00000000..229853cc
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.allow_unrelated_perms.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the ClusterRole does not allow pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
diff --git a/test/checks/clusterrolePodExecAttach/success.attach_wrong_verb.yaml b/test/checks/clusterrolePodExecAttach/success.attach_wrong_verb.yaml
new file mode 100644
index 00000000..96f414a7
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.attach_wrong_verb.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the ClusterRole allows pods/attach but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_admin.yaml b/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_admin.yaml
new file mode 100644
index 00000000..f4b13166
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_admin.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the ClusterRole is an expected default one (by name).
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: admin
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "create" ]
diff --git a/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_clusteradmin.yaml b/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_clusteradmin.yaml
new file mode 100644
index 00000000..89e59c5c
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.defaultclusterrole_clusteradmin.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the ClusterRole is an expected default one (by name).
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: cluster-admin
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "create" ]
diff --git a/test/checks/clusterrolePodExecAttach/success.exec_wrong_verb.yaml b/test/checks/clusterrolePodExecAttach/success.exec_wrong_verb.yaml
new file mode 100644
index 00000000..a2c74a3e
--- /dev/null
+++ b/test/checks/clusterrolePodExecAttach/success.exec_wrong_verb.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the ClusterRole allows pods/exec but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ list ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.all_wildcards.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.all_wildcards.yaml
new file mode 100644
index 00000000..26d4a61f
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.all_wildcards.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole apiGroups, resources, and verbs are all * which allows pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.attach_explicit.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.attach_explicit.yaml
new file mode 100644
index 00000000..dd764f82
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.attach_explicit.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.attach_using_verb_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.attach_using_verb_wildcard.yaml
new file mode 100644
index 00000000..518b5a54
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.attach_using_verb_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.binding_points_to_missing_ref.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.binding_points_to_missing_ref.yaml
new file mode 100644
index 00000000..83ba5c0f
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.binding_points_to_missing_ref.yaml
@@ -0,0 +1,13 @@
+# This fails because the roleRef points to a ClusterRole that does not exist.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: does-not-exist
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..744cddbf
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..3a2eb69b
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..17095f6f
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.exec_explicit.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.exec_explicit.yaml
new file mode 100644
index 00000000..83a00847
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.exec_explicit.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/exec.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.exec_verb_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.exec_verb_wildcard.yaml
new file mode 100644
index 00000000..9033e423
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.exec_verb_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/exec using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..19bda2bf
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..5479c40a
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml b/test/checks/clusterrolebindingPodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..a0322012
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,22 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.allow_unrelated_perms.yaml b/test/checks/clusterrolebindingPodExecAttach/success.allow_unrelated_perms.yaml
new file mode 100644
index 00000000..d60d661e
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.allow_unrelated_perms.yaml
@@ -0,0 +1,22 @@
+# This succeeds because the ClusterRole does not allow pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.attach_wrong_verb.yaml b/test/checks/clusterrolebindingPodExecAttach/success.attach_wrong_verb.yaml
new file mode 100644
index 00000000..23c67957
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.attach_wrong_verb.yaml
@@ -0,0 +1,22 @@
+# This succeeds because the ClusterRole allows pods/attach but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_clusteradmin.yaml b/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_clusteradmin.yaml
new file mode 100644
index 00000000..201f8f92
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_clusteradmin.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the ClusterRoleBinding is a default one (by name).
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: cluster-admin
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: cluster-admin
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This ClusterRole satisfies the above binding.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: cluster-admin
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "create" ]
+---
+  # This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ "get" ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_system-controller-generic-garbage-collector.yaml b/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_system-controller-generic-garbage-collector.yaml
new file mode 100644
index 00000000..fb0023a3
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.defaultclusterrolebinding_system-controller-generic-garbage-collector.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the ClusterRoleBinding is a default one (by name).
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: system:controller:generic-garbage-collector
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: system:controller:generic-garbage-collector
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
+---
+# This ClusterRole satisfies the above binding.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: system:controller:generic-garbage-collector
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "create" ]
+---
+  # This Role exists so there is at least one Role for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: not-used
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ "get" ]
diff --git a/test/checks/clusterrolebindingPodExecAttach/success.exec_wrong_verb.yaml b/test/checks/clusterrolebindingPodExecAttach/success.exec_wrong_verb.yaml
new file mode 100644
index 00000000..6370b0b6
--- /dev/null
+++ b/test/checks/clusterrolebindingPodExecAttach/success.exec_wrong_verb.yaml
@@ -0,0 +1,22 @@
+# This succeeds because the ClusterRole allows pods/exec but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolePodExecAttach/failure.all_wildcards.yaml b/test/checks/rolePodExecAttach/failure.all_wildcards.yaml
new file mode 100644
index 00000000..446858f5
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.all_wildcards.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role apiGroups, resources, and verbs are all * which allows pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
diff --git a/test/checks/rolePodExecAttach/failure.attach_explicit.yaml b/test/checks/rolePodExecAttach/failure.attach_explicit.yaml
new file mode 100644
index 00000000..ac385dea
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.attach_explicit.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
diff --git a/test/checks/rolePodExecAttach/failure.attach_using_verb_wildcard.yaml b/test/checks/rolePodExecAttach/failure.attach_using_verb_wildcard.yaml
new file mode 100644
index 00000000..a472ced8
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.attach_using_verb_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "*" ]
diff --git a/test/checks/rolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..7abc4260
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ create ]
diff --git a/test/checks/rolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml b/test/checks/rolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..e32b91ed
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
diff --git a/test/checks/rolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml b/test/checks/rolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..a163dc77
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
diff --git a/test/checks/rolePodExecAttach/failure.exec_explicit.yaml b/test/checks/rolePodExecAttach/failure.exec_explicit.yaml
new file mode 100644
index 00000000..70ea5fcc
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.exec_explicit.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/exec.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
diff --git a/test/checks/rolePodExecAttach/failure.exec_verb_wildcard.yaml b/test/checks/rolePodExecAttach/failure.exec_verb_wildcard.yaml
new file mode 100644
index 00000000..afaa8b58
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.exec_verb_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/exec using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "*" ]
diff --git a/test/checks/rolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..656b7a04
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ get ]
diff --git a/test/checks/rolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml b/test/checks/rolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..e488d523
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ get ]
diff --git a/test/checks/rolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml b/test/checks/rolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..e5e8841a
--- /dev/null
+++ b/test/checks/rolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,10 @@
+# This fails because the Role allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ get ]
diff --git a/test/checks/rolePodExecAttach/success.allow_unrelated_perms.yaml b/test/checks/rolePodExecAttach/success.allow_unrelated_perms.yaml
new file mode 100644
index 00000000..735362a5
--- /dev/null
+++ b/test/checks/rolePodExecAttach/success.allow_unrelated_perms.yaml
@@ -0,0 +1,10 @@
+# This succeeds because the Role does not allow pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
diff --git a/test/checks/rolePodExecAttach/success.attach_wrong_verb.yaml b/test/checks/rolePodExecAttach/success.attach_wrong_verb.yaml
new file mode 100644
index 00000000..9c9da717
--- /dev/null
+++ b/test/checks/rolePodExecAttach/success.attach_wrong_verb.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the Role allows pods/attach but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ list ]
diff --git a/test/checks/rolePodExecAttach/success.exec_wrong_verb.yaml b/test/checks/rolePodExecAttach/success.exec_wrong_verb.yaml
new file mode 100644
index 00000000..fd5cf9df
--- /dev/null
+++ b/test/checks/rolePodExecAttach/success.exec_wrong_verb.yaml
@@ -0,0 +1,9 @@
+# This succeeds because the Role allows pods/exec but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ list ]
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.all_wildcards.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.all_wildcards.yaml
new file mode 100644
index 00000000..076cf114
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.all_wildcards.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole apiGroups, resources, and verbs are all * which allows pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_explicit.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_explicit.yaml
new file mode 100644
index 00000000..2a64a81f
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_explicit.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_using_verb_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_using_verb_wildcard.yaml
new file mode 100644
index 00000000..0128a02e
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.attach_using_verb_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_points_to_missing_ref.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_points_to_missing_ref.yaml
new file mode 100644
index 00000000..520ca8a0
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.binding_points_to_missing_ref.yaml
@@ -0,0 +1,14 @@
+# This fails because the roleRef points to a ClusterRole that does not exist.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: does-not-exist
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..a1028203
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..475aa542
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..c1661f44
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_explicit.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_explicit.yaml
new file mode 100644
index 00000000..038179bb
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_explicit.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/exec.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_verb_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_verb_wildcard.yaml
new file mode 100644
index 00000000..dad55fcf
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.exec_verb_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/exec using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..c6af9cba
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..c33a99ac
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..9a8182a2
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,23 @@
+# This fails because the ClusterRole allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.allow_unrelated_perms.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.allow_unrelated_perms.yaml
new file mode 100644
index 00000000..1bdf0dd8
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.allow_unrelated_perms.yaml
@@ -0,0 +1,23 @@
+# This succeeds because the ClusterRole does not allow pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.attach_wrong_verb.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.attach_wrong_verb.yaml
new file mode 100644
index 00000000..51fe2390
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.attach_wrong_verb.yaml
@@ -0,0 +1,23 @@
+# This succeeds because the ClusterRole allows pods/attach but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.binding_references_role.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.binding_references_role.yaml
new file mode 100644
index 00000000..d4dec6b5
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.binding_references_role.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the roleBinding references a Role instead of a ClusterRole.
+# THis ClusterRole exists so there is at leat one ClusterRole for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: not-used
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingClusterRolePodExecAttach/success.exec_wrong_verb.yaml b/test/checks/rolebindingClusterRolePodExecAttach/success.exec_wrong_verb.yaml
new file mode 100644
index 00000000..2e175c22
--- /dev/null
+++ b/test/checks/rolebindingClusterRolePodExecAttach/success.exec_wrong_verb.yaml
@@ -0,0 +1,23 @@
+# This succeeds because the ClusterRole allows pods/exec but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.all_wildcards.yaml b/test/checks/rolebindingRolePodExecAttach/failure.all_wildcards.yaml
new file mode 100644
index 00000000..b83f09fc
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.all_wildcards.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role apiGroups, resources, and verbs are all * which allows pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "*" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.attach_explicit.yaml b/test/checks/rolebindingRolePodExecAttach/failure.attach_explicit.yaml
new file mode 100644
index 00000000..1f246b72
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.attach_explicit.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.attach_using_verb_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.attach_using_verb_wildcard.yaml
new file mode 100644
index 00000000..d38c56db
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.attach_using_verb_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.binding_points_to_missing_ref.yaml b/test/checks/rolebindingRolePodExecAttach/failure.binding_points_to_missing_ref.yaml
new file mode 100644
index 00000000..95777c59
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.binding_points_to_missing_ref.yaml
@@ -0,0 +1,14 @@
+# This fails because the roleRef points to a Role that does not exist.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: does-not-exist
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..a6e22bce
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.create_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..e952a862
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.create_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..ee614e35
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.create_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.exec_explicit.yaml b/test/checks/rolebindingRolePodExecAttach/failure.exec_explicit.yaml
new file mode 100644
index 00000000..1de8352b
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.exec_explicit.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/exec.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.exec_verb_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.exec_verb_wildcard.yaml
new file mode 100644
index 00000000..448d5d11
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.exec_verb_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/exec using all verbs.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ "*" ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
new file mode 100644
index 00000000..56efc20a
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.get_attach_and_exec_using_resource_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach|exec using all resources.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "*" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..a82320c5
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.get_attach_using_apigroup_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/attach using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/attach" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml b/test/checks/rolebindingRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
new file mode 100644
index 00000000..67bd97da
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/failure.get_exec_using_apigroup_wildcard.yaml
@@ -0,0 +1,24 @@
+# This fails because the Role allows pods/exec using all apiGroups.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "*" ]
+    resources: [ "pods/exec" ]
+    verbs: [ get ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.allow_unrelated_perms.yaml b/test/checks/rolebindingRolePodExecAttach/success.allow_unrelated_perms.yaml
new file mode 100644
index 00000000..519a9781
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.allow_unrelated_perms.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the Role does not allow pods/exec|attach.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.attach_wrong_verb.yaml b/test/checks/rolebindingRolePodExecAttach/success.attach_wrong_verb.yaml
new file mode 100644
index 00000000..37f35d3f
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.attach_wrong_verb.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the Role allows pods/attach but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/attach" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.binding_references_role.yaml b/test/checks/rolebindingRolePodExecAttach/success.binding_references_role.yaml
new file mode 100644
index 00000000..d4dec6b5
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.binding_references_role.yaml
@@ -0,0 +1,34 @@
+# This succeeds because the roleBinding references a Role instead of a ClusterRole.
+# THis ClusterRole exists so there is at leat one ClusterRole for the additionalSchema to find.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: not-used
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods" ]
+    verbs: [ get, create ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser
diff --git a/test/checks/rolebindingRolePodExecAttach/success.exec_wrong_verb.yaml b/test/checks/rolebindingRolePodExecAttach/success.exec_wrong_verb.yaml
new file mode 100644
index 00000000..cc09e09c
--- /dev/null
+++ b/test/checks/rolebindingRolePodExecAttach/success.exec_wrong_verb.yaml
@@ -0,0 +1,24 @@
+# This succeeds because the Role allows pods/exec but with a safe verb.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: test
+  namespace: test
+rules:
+  - apiGroups: [ "" ]
+    resources: [ "pods/exec" ]
+    verbs: [ list ]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: test
+  namespace: test
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: test
+subjects:
+- apiGroup: rbac.authorization.k8s.io
+  kind: User
+  name: testuser