Repository: cloud-custodian/cloud-custodian

Issue:
Add KMS condition key handlers for cross-account filter
### Describe the feature

Our KMS keys are often written with multiple condition keys, namely kms:CallerAccount combined with either kms:GrantOperations or kms:ViaService.

Similar to how AWS documents it:
https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-caller-account

These are not currently supported and cause false-positives for the cross-account filter.

### Extra information or context

_No response_


Discussion:
Can assign to me :)

Resolving patch:
diff --git a/c7n/filters/iamaccess.py b/c7n/filters/iamaccess.py
--- a/c7n/filters/iamaccess.py
+++ b/c7n/filters/iamaccess.py
@@ -210,10 +210,6 @@ def normalize_conditions(self, s):
 
     # Condition handlers
 
-    # kms specific
-    def handle_kms_calleraccount(self, s, c):
-        return bool(set(map(_account, c['values'])).difference(self.allowed_accounts))
-
     # sns default policy
     def handle_aws_sourceowner(self, s, c):
         return bool(set(map(_account, c['values'])).difference(self.allowed_accounts))
diff --git a/c7n/resources/kms.py b/c7n/resources/kms.py
--- a/c7n/resources/kms.py
+++ b/c7n/resources/kms.py
@@ -1,5 +1,6 @@
 # Copyright The Cloud Custodian Authors.
 # SPDX-License-Identifier: Apache-2.0
+from c7n.filters.iamaccess import _account, PolicyChecker
 from botocore.exceptions import ClientError
 
 from datetime import datetime, timezone
@@ -176,6 +177,21 @@ def _key_rotation_status(resource):
                 r.get('KeyRotationEnabled', {}))]
 
 
+class KMSPolicyChecker(PolicyChecker):
+    # https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms
+
+    def handle_kms_calleraccount(self, s, c):
+        return bool(set(map(_account, c['values'])).difference(self.allowed_accounts))
+
+    def handle_kms_viaservice(self, s, c):
+        # We dont filter on service so all are presumed allowed
+        return False
+
+    def handle_kms_grantoperations(self, s, c):
+        # We dont filter on GrantOperations so all are presumed allowed
+        return False
+
+
 @Key.filter_registry.register('cross-account')
 @KeyAlias.filter_registry.register('cross-account')
 class KMSCrossAccountAccessFilter(CrossAccountAccessFilter):
@@ -193,6 +209,8 @@ class KMSCrossAccountAccessFilter(CrossAccountAccessFilter):
     """
     permissions = ('kms:GetKeyPolicy',)
 
+    checker_factory = KMSPolicyChecker
+
     def process(self, resources, event=None):
         client = local_session(
             self.manager.session_factory).client('kms')


Test patch:
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.CreateKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.CreateKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.CreateKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "598c62cd-5123-43e2-a519-60b3b89ad6ab",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/598c62cd-5123-43e2-a519-60b3b89ad6ab",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 25,
+                "microsecond": 766000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-2",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DescribeKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DescribeKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "598c62cd-5123-43e2-a519-60b3b89ad6ab",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/598c62cd-5123-43e2-a519-60b3b89ad6ab",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 25,
+                "microsecond": 766000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-2",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DisableKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DisableKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.DisableKey_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.GetKeyPolicy_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.GetKeyPolicy_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.GetKeyPolicy_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Policy": "{\n  \"Version\" : \"2012-10-17\",\n  \"Id\" : \"Lulu\",\n  \"Statement\" : [ {\n    \"Sid\" : \"Enable IAM User Permissions\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"arn:aws:iam::644160558196:root\"\n    },\n    \"Action\" : \"kms:*\",\n    \"Resource\" : \"*\"\n  }, {\n    \"Sid\" : \"Good condition key\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"*\"\n    },\n    \"Action\" : \"kms:CreateGrant\",\n    \"Resource\" : \"*\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"kms:CallerAccount\" : \"644160558196\"\n      }\n    }\n  } ]\n}",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListAliases_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListAliases_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListAliases_1.json
@@ -0,0 +1,78 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/aws/dynamodb",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/dynamodb"
+            },
+            {
+                "AliasName": "alias/aws/ebs",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ebs",
+                "TargetKeyId": "0a22fb1d-3cb7-4555-aa4b-c6fc2c97e13f",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                }
+            },
+            {
+                "AliasName": "alias/aws/elasticfilesystem",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/elasticfilesystem"
+            },
+            {
+                "AliasName": "alias/aws/es",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/es"
+            },
+            {
+                "AliasName": "alias/aws/glue",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/glue"
+            },
+            {
+                "AliasName": "alias/aws/kinesisvideo",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/kinesisvideo"
+            },
+            {
+                "AliasName": "alias/aws/rds",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/rds"
+            },
+            {
+                "AliasName": "alias/aws/redshift",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshift"
+            },
+            {
+                "AliasName": "alias/aws/redshifttest",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshifttest"
+            },
+            {
+                "AliasName": "alias/aws/s3",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/s3"
+            },
+            {
+                "AliasName": "alias/aws/ssm",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ssm"
+            },
+            {
+                "AliasName": "alias/aws/xray",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/xray"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListKeys_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListKeys_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ListKeys_1.json
@@ -0,0 +1,13 @@
+{
+    "status_code": 200,
+    "data": {
+        "Keys": [
+            {
+                "KeyId": "598c62cd-5123-43e2-a519-60b3b89ad6ab",
+                "KeyArn": "arn:aws:kms:af-south-1:644160558196:key/598c62cd-5123-43e2-a519-60b3b89ad6ab"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ScheduleKeyDeletion_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ScheduleKeyDeletion_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/kms.ScheduleKeyDeletion_1.json
@@ -0,0 +1,19 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyId": "arn:aws:kms:af-south-1:644160558196:key/598c62cd-5123-43e2-a519-60b3b89ad6ab",
+        "DeletionDate": {
+            "__class__": "datetime",
+            "year": 2022,
+            "month": 8,
+            "day": 29,
+            "hour": 10,
+            "minute": 25,
+            "second": 26,
+            "microsecond": 467000
+        },
+        "KeyState": "PendingDeletion",
+        "PendingWindowInDays": 7,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_1/tagging.GetResources_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_1/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_1/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.CreateKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.CreateKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.CreateKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 26,
+                "microsecond": 681000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-3",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DescribeKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DescribeKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 26,
+                "microsecond": 681000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-3",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DisableKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DisableKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.DisableKey_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.GetKeyPolicy_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.GetKeyPolicy_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.GetKeyPolicy_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Policy": "{\n  \"Version\" : \"2012-10-17\",\n  \"Id\" : \"Lulu\",\n  \"Statement\" : [ {\n    \"Sid\" : \"Enable IAM User Permissions\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"arn:aws:iam::644160558196:root\"\n    },\n    \"Action\" : \"kms:*\",\n    \"Resource\" : \"*\"\n  }, {\n    \"Sid\" : \"Good condition key\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"arn:aws:iam::644160558196:root\"\n    },\n    \"Action\" : \"kms:Encrypt\",\n    \"Resource\" : \"*\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"kms:ViaService\" : \"s3.af-south-1.amazonaws.com\"\n      },\n      \"ForAllValues:StringEquals\" : {\n        \"kms:GrantOperations\" : [ \"Encrypt\", \"ReEncryptTo\" ]\n      }\n    }\n  } ]\n}",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListAliases_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListAliases_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListAliases_1.json
@@ -0,0 +1,78 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/aws/dynamodb",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/dynamodb"
+            },
+            {
+                "AliasName": "alias/aws/ebs",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ebs",
+                "TargetKeyId": "0a22fb1d-3cb7-4555-aa4b-c6fc2c97e13f",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                }
+            },
+            {
+                "AliasName": "alias/aws/elasticfilesystem",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/elasticfilesystem"
+            },
+            {
+                "AliasName": "alias/aws/es",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/es"
+            },
+            {
+                "AliasName": "alias/aws/glue",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/glue"
+            },
+            {
+                "AliasName": "alias/aws/kinesisvideo",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/kinesisvideo"
+            },
+            {
+                "AliasName": "alias/aws/rds",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/rds"
+            },
+            {
+                "AliasName": "alias/aws/redshift",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshift"
+            },
+            {
+                "AliasName": "alias/aws/redshifttest",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshifttest"
+            },
+            {
+                "AliasName": "alias/aws/s3",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/s3"
+            },
+            {
+                "AliasName": "alias/aws/ssm",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ssm"
+            },
+            {
+                "AliasName": "alias/aws/xray",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/xray"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListKeys_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListKeys_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ListKeys_1.json
@@ -0,0 +1,13 @@
+{
+    "status_code": 200,
+    "data": {
+        "Keys": [
+            {
+                "KeyId": "ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+                "KeyArn": "arn:aws:kms:af-south-1:644160558196:key/ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ScheduleKeyDeletion_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ScheduleKeyDeletion_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/kms.ScheduleKeyDeletion_1.json
@@ -0,0 +1,19 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyId": "arn:aws:kms:af-south-1:644160558196:key/ba74e5eb-e6f6-4a7c-9732-8c49f4d9caa7",
+        "DeletionDate": {
+            "__class__": "datetime",
+            "year": 2022,
+            "month": 8,
+            "day": 29,
+            "hour": 10,
+            "minute": 25,
+            "second": 27,
+            "microsecond": 480000
+        },
+        "KeyState": "PendingDeletion",
+        "PendingWindowInDays": 7,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_2/tagging.GetResources_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_2/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_2/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.CreateKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.CreateKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.CreateKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "a330b187-ba70-4a78-b5ef-26e96f3c106f",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/a330b187-ba70-4a78-b5ef-26e96f3c106f",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 27,
+                "microsecond": 668000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-4",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DescribeKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DescribeKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DescribeKey_1.json
@@ -0,0 +1,33 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyMetadata": {
+            "AWSAccountId": "644160558196",
+            "KeyId": "a330b187-ba70-4a78-b5ef-26e96f3c106f",
+            "Arn": "arn:aws:kms:af-south-1:644160558196:key/a330b187-ba70-4a78-b5ef-26e96f3c106f",
+            "CreationDate": {
+                "__class__": "datetime",
+                "year": 2022,
+                "month": 8,
+                "day": 22,
+                "hour": 10,
+                "minute": 25,
+                "second": 27,
+                "microsecond": 668000
+            },
+            "Enabled": true,
+            "Description": "test-cross-account-4",
+            "KeyUsage": "ENCRYPT_DECRYPT",
+            "KeyState": "Enabled",
+            "Origin": "AWS_KMS",
+            "KeyManager": "CUSTOMER",
+            "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
+            "KeySpec": "SYMMETRIC_DEFAULT",
+            "EncryptionAlgorithms": [
+                "SYMMETRIC_DEFAULT"
+            ],
+            "MultiRegion": false
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DisableKey_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DisableKey_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.DisableKey_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.GetKeyPolicy_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.GetKeyPolicy_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.GetKeyPolicy_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Policy": "{\n  \"Version\" : \"2012-10-17\",\n  \"Id\" : \"Lulu\",\n  \"Statement\" : [ {\n    \"Sid\" : \"Enable IAM User Permissions\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"arn:aws:iam::644160558196:root\"\n    },\n    \"Action\" : \"kms:*\",\n    \"Resource\" : \"*\"\n  }, {\n    \"Sid\" : \"Bad condition key\",\n    \"Effect\" : \"Allow\",\n    \"Principal\" : {\n      \"AWS\" : \"*\"\n    },\n    \"Action\" : \"kms:Encrypt\",\n    \"Resource\" : \"*\",\n    \"Condition\" : {\n      \"StringEquals\" : {\n        \"kms:CallerAccount\" : \"*\",\n        \"kms:ViaService\" : \"s3.af-south-1.amazonaws.com\"\n      },\n      \"ForAllValues:StringEquals\" : {\n        \"kms:GrantOperations\" : \"Encrypt\"\n      }\n    }\n  } ]\n}",
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListAliases_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListAliases_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListAliases_1.json
@@ -0,0 +1,78 @@
+{
+    "status_code": 200,
+    "data": {
+        "Aliases": [
+            {
+                "AliasName": "alias/aws/dynamodb",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/dynamodb"
+            },
+            {
+                "AliasName": "alias/aws/ebs",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ebs",
+                "TargetKeyId": "0a22fb1d-3cb7-4555-aa4b-c6fc2c97e13f",
+                "CreationDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                },
+                "LastUpdatedDate": {
+                    "__class__": "datetime",
+                    "year": 2022,
+                    "month": 6,
+                    "day": 5,
+                    "hour": 15,
+                    "minute": 22,
+                    "second": 20,
+                    "microsecond": 661000
+                }
+            },
+            {
+                "AliasName": "alias/aws/elasticfilesystem",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/elasticfilesystem"
+            },
+            {
+                "AliasName": "alias/aws/es",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/es"
+            },
+            {
+                "AliasName": "alias/aws/glue",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/glue"
+            },
+            {
+                "AliasName": "alias/aws/kinesisvideo",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/kinesisvideo"
+            },
+            {
+                "AliasName": "alias/aws/rds",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/rds"
+            },
+            {
+                "AliasName": "alias/aws/redshift",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshift"
+            },
+            {
+                "AliasName": "alias/aws/redshifttest",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/redshifttest"
+            },
+            {
+                "AliasName": "alias/aws/s3",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/s3"
+            },
+            {
+                "AliasName": "alias/aws/ssm",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/ssm"
+            },
+            {
+                "AliasName": "alias/aws/xray",
+                "AliasArn": "arn:aws:kms:af-south-1:644160558196:alias/aws/xray"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListKeys_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListKeys_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ListKeys_1.json
@@ -0,0 +1,13 @@
+{
+    "status_code": 200,
+    "data": {
+        "Keys": [
+            {
+                "KeyId": "a330b187-ba70-4a78-b5ef-26e96f3c106f",
+                "KeyArn": "arn:aws:kms:af-south-1:644160558196:key/a330b187-ba70-4a78-b5ef-26e96f3c106f"
+            }
+        ],
+        "Truncated": false,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ScheduleKeyDeletion_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ScheduleKeyDeletion_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/kms.ScheduleKeyDeletion_1.json
@@ -0,0 +1,19 @@
+{
+    "status_code": 200,
+    "data": {
+        "KeyId": "arn:aws:kms:af-south-1:644160558196:key/a330b187-ba70-4a78-b5ef-26e96f3c106f",
+        "DeletionDate": {
+            "__class__": "datetime",
+            "year": 2022,
+            "month": 8,
+            "day": 29,
+            "hour": 10,
+            "minute": 25,
+            "second": 28,
+            "microsecond": 409000
+        },
+        "KeyState": "PendingDeletion",
+        "PendingWindowInDays": 7,
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_cross_account_kms_condition_keys_3/tagging.GetResources_1.json b/tests/data/placebo/test_cross_account_kms_condition_keys_3/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_cross_account_kms_condition_keys_3/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_iam.py b/tests/test_iam.py
--- a/tests/test_iam.py
+++ b/tests/test_iam.py
@@ -560,7 +560,7 @@ def test_iam_role_unused(self):
         self.assertEqual(len(resources), 1)
         self.assertEqual(
             p.resource_manager.get_arns(resources),
-            ['arn:aws:iam::644160558196:role/service-role/AmazonSageMaker-ExecutionRole-20180108T122369']) # NOQA
+            ['arn:aws:iam::644160558196:role/service-role/AmazonSageMaker-ExecutionRole-20180108T122369'])  # NOQA
 
         self.assertDeprecation(p, """
             policy 'iam-inuse-role'
@@ -1624,60 +1624,6 @@ def test_iam_group_delete_inline_policies(self):
         self.assertEqual(len(inline_policies_after["PolicyNames"]), 0)
 
 
-class KMSCrossAccount(BaseTest):
-
-    def test_kms_cross_account(self):
-        self.patch(CrossAccountAccessFilter, "executor_factory", MainThreadExecutor)
-        session_factory = self.replay_flight_data("test_cross_account_kms")
-        client = session_factory().client("kms")
-
-        policy = {
-            "Id": "Lulu",
-            "Version": "2012-10-17",
-            "Statement": [
-                {
-                    "Sid": "Enable IAM User Permissions",
-                    "Effect": "Allow",
-                    "Principal": {"AWS": "arn:aws:iam::644160558196:root"},
-                    "Action": "kms:*",
-                    "Resource": "*",
-                },
-                {
-                    "Sid": "Enable Cross Account",
-                    "Effect": "Allow",
-                    "Principal": "*",
-                    "Action": "kms:Encrypt",
-                    "Resource": "*",
-                },
-            ],
-        }
-
-        key_info = client.create_key(
-            Policy=json.dumps(policy), Description="test-cross-account-3"
-        )[
-            "KeyMetadata"
-        ]
-
-        # disable and schedule deletion
-        self.addCleanup(
-            client.schedule_key_deletion, KeyId=key_info["KeyId"], PendingWindowInDays=7
-        )
-        self.addCleanup(client.disable_key, KeyId=key_info["KeyId"])
-
-        p = self.load_policy(
-            {
-                "name": "kms-cross",
-                "resource": "kms-key",
-                "filters": [{"KeyState": "Enabled"}, "cross-account"],
-            },
-            session_factory=session_factory,
-        )
-
-        resources = p.run()
-        self.assertEqual(len(resources), 1)
-        self.assertEqual(resources[0]["KeyId"], key_info["KeyId"])
-
-
 class GlacierCrossAccount(BaseTest):
 
     def test_glacier_cross_account(self):
diff --git a/tests/test_kms.py b/tests/test_kms.py
--- a/tests/test_kms.py
+++ b/tests/test_kms.py
@@ -7,6 +7,9 @@
 from c7n.resources.aws import shape_validate
 from .common import BaseTest, functional
 
+from c7n.executor import MainThreadExecutor
+from c7n.filters.iamaccess import CrossAccountAccessFilter
+
 
 class KMSTest(BaseTest):
 
@@ -388,3 +391,249 @@ def test_kms_post_finding(self):
 
         shape_validate(
             rfinding['Details']['AwsKmsKey'], 'AwsKmsKeyDetails', 'securityhub')
+
+
+class KMSCrossAccount(BaseTest):
+
+    def test_kms_cross_account(self):
+        self.patch(CrossAccountAccessFilter, "executor_factory", MainThreadExecutor)
+        session_factory = self.replay_flight_data("test_cross_account_kms")
+        client = session_factory().client("kms")
+
+        policy = {
+            "Id": "Lulu",
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "Enable IAM User Permissions",
+                    "Effect": "Allow",
+                    "Principal": {"AWS": "arn:aws:iam::644160558196:root"},
+                    "Action": "kms:*",
+                    "Resource": "*",
+                },
+                {
+                    "Sid": "Enable Cross Account",
+                    "Effect": "Allow",
+                    "Principal": "*",
+                    "Action": "kms:Encrypt",
+                    "Resource": "*",
+                },
+            ],
+        }
+
+        key_info = client.create_key(
+            Policy=json.dumps(policy), Description="test-cross-account-1"
+        )[
+            "KeyMetadata"
+        ]
+
+        # disable and schedule deletion
+        self.addCleanup(
+            client.schedule_key_deletion, KeyId=key_info["KeyId"], PendingWindowInDays=7
+        )
+        self.addCleanup(client.disable_key, KeyId=key_info["KeyId"])
+
+        p = self.load_policy(
+            {
+                "name": "kms-cross",
+                "resource": "kms-key",
+                "filters": [{"KeyState": "Enabled"}, "cross-account"],
+            },
+            session_factory=session_factory,
+            config={"region": "us-east-1"}
+        )
+
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(resources[0]["KeyId"], key_info["KeyId"])
+
+    def test_kms_cross_account_condition_keys_1(self):
+        self.patch(CrossAccountAccessFilter, "executor_factory", MainThreadExecutor)
+        session_factory = self.replay_flight_data(
+            "test_cross_account_kms_condition_keys_1", region="af-south-1")
+        client = session_factory().client("kms")
+
+        policy = {
+            "Id": "Lulu",
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "Enable IAM User Permissions",
+                    "Effect": "Allow",
+                    "Principal": {"AWS": "arn:aws:iam::644160558196:root"},
+                    "Action": "kms:*",
+                    "Resource": "*",
+                },
+                {
+                    "Sid": "Good condition key",
+                    "Effect": "Allow",
+                    "Principal": {
+                        "AWS": "*"
+                    },
+                    "Action": "kms:CreateGrant",
+                    "Resource": "*",
+                    "Condition": {
+                        "StringEquals": {
+                            "kms:CallerAccount": "644160558196"
+                        }
+                    }
+                },
+            ],
+        }
+
+        key_info = client.create_key(
+            Policy=json.dumps(policy), Description="test-cross-account-2"
+        )[
+            "KeyMetadata"
+        ]
+
+        # disable and schedule deletion
+        self.addCleanup(
+            client.schedule_key_deletion, KeyId=key_info["KeyId"], PendingWindowInDays=7
+        )
+        self.addCleanup(client.disable_key, KeyId=key_info["KeyId"])
+
+        p = self.load_policy(
+            {
+                "name": "kms-cross",
+                "resource": "kms-key",
+                "filters": [{"KeyState": "Enabled"}, "cross-account"],
+            },
+            session_factory=session_factory,
+            config={"region": "af-south-1"}
+        )
+
+        resources = p.run()
+        self.assertEqual(len(resources), 0)
+
+    def test_kms_cross_account_condition_keys_2(self):
+        self.patch(CrossAccountAccessFilter, "executor_factory", MainThreadExecutor)
+        session_factory = self.replay_flight_data(
+            "test_cross_account_kms_condition_keys_2", region="af-south-1")
+        client = session_factory().client("kms")
+
+        policy = {
+            "Id": "Lulu",
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "Enable IAM User Permissions",
+                    "Effect": "Allow",
+                    "Principal": {"AWS": "arn:aws:iam::644160558196:root"},
+                    "Action": "kms:*",
+                    "Resource": "*",
+                },
+                {
+                    "Sid": "Good condition key",
+                    "Effect": "Allow",
+                    "Principal": {
+                        "AWS": "644160558196"
+                    },
+                    "Action": "kms:Encrypt",
+                    "Resource": "*",
+                    "Condition": {
+                        "StringEquals": {
+                            "kms:ViaService": "s3.af-south-1.amazonaws.com",
+                        },
+                        "ForAllValues:StringEquals": {
+                            "kms:GrantOperations": [
+                                "Encrypt",
+                                "ReEncryptTo"
+                            ]
+                        }
+
+                    }
+                },
+            ],
+        }
+
+        key_info = client.create_key(
+            Policy=json.dumps(policy), Description="test-cross-account-3"
+        )[
+            "KeyMetadata"
+        ]
+
+        # disable and schedule deletion
+        self.addCleanup(
+            client.schedule_key_deletion, KeyId=key_info["KeyId"], PendingWindowInDays=7
+        )
+        self.addCleanup(client.disable_key, KeyId=key_info["KeyId"])
+
+        p = self.load_policy(
+            {
+                "name": "kms-cross",
+                "resource": "kms-key",
+                "filters": [{"KeyState": "Enabled"}, "cross-account"],
+            },
+            session_factory=session_factory,
+            config={"region": "af-south-1"}
+        )
+
+        resources = p.run()
+        self.assertEqual(len(resources), 0)
+
+    def test_kms_cross_account_condition_keys_3(self):
+        self.patch(CrossAccountAccessFilter, "executor_factory", MainThreadExecutor)
+        session_factory = self.replay_flight_data(
+            "test_cross_account_kms_condition_keys_3", region="af-south-1")
+        client = session_factory().client("kms")
+
+        policy = {
+            "Id": "Lulu",
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Sid": "Enable IAM User Permissions",
+                    "Effect": "Allow",
+                    "Principal": {"AWS": "arn:aws:iam::644160558196:root"},
+                    "Action": "kms:*",
+                    "Resource": "*",
+                },
+                {
+                    "Sid": "Bad condition key",
+                    "Effect": "Allow",
+                    "Principal": {
+                        "AWS": "*"
+                    },
+                    "Action": "kms:Encrypt",
+                    "Resource": "*",
+                    "Condition": {
+                        "StringEquals": {
+                            "kms:ViaService": "s3.af-south-1.amazonaws.com",
+                            "kms:CallerAccount": "*",
+                        },
+                        "ForAllValues:StringEquals": {
+                            "kms:GrantOperations": [
+                                "Encrypt"
+                            ]
+                        }
+                    }
+                },
+            ],
+        }
+
+        key_info = client.create_key(
+            Policy=json.dumps(policy), Description="test-cross-account-4"
+        )[
+            "KeyMetadata"
+        ]
+
+        # disable and schedule deletion
+        self.addCleanup(
+            client.schedule_key_deletion, KeyId=key_info["KeyId"], PendingWindowInDays=7
+        )
+        self.addCleanup(client.disable_key, KeyId=key_info["KeyId"])
+
+        p = self.load_policy(
+            {
+                "name": "kms-cross",
+                "resource": "kms-key",
+                "filters": [{"KeyState": "Enabled"}, "cross-account"],
+            },
+            session_factory=session_factory,
+            config={"region": "af-south-1"}
+        )
+
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        self.assertEqual(resources[0]["KeyId"], key_info["KeyId"])