We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Key error when adding a policy statement to an SNS Topic
### Describe the bug

When adding/describing a statement to an SNS topic a key error is thrown when attempting to retrieve the `Sid` of the policy.

### What did you expect to happen?

A policy statement should be described and added without error

### Cloud Provider

Amazon Web Services (AWS)

### Cloud Custodian version and dependency information

```shell
Custodian:   0.9.13
Python:      3.8.11 (default, Jun 29 2021, 03:17:05) 
             [Clang 12.0.0 (clang-1200.0.32.29)]
Platform:    posix.uname_result(sysname='Darwin', nodename='f8ffc250c2ee', release='20.6.0', version='Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64', machine='x86_64')
Using venv:  True
Docker: False
Installed: 

PyJWT==1.7.1
PyYAML==5.4.1
adal==1.2.7
appdirs==1.4.4
applicationinsights==0.11.10
apscheduler==3.7.0
argcomplete==1.12.3
attrs==21.2.0
azure-common==1.1.27
azure-core==1.15.0
azure-cosmos==3.2.0
azure-cosmosdb-nspkg==2.0.2
azure-cosmosdb-table==1.0.6
azure-functions==1.7.2
azure-graphrbac==0.61.1
azure-identity==1.6.0
azure-keyvault==4.1.0
azure-keyvault-certificates==4.3.0
azure-keyvault-keys==4.4.0
azure-keyvault-secrets==4.3.0
azure-mgmt-apimanagement==1.0.0
azure-mgmt-applicationinsights==1.0.0
azure-mgmt-authorization==1.0.0
azure-mgmt-batch==15.0.0
azure-mgmt-cdn==10.0.0
azure-mgmt-cognitiveservices==11.0.0
azure-mgmt-compute==19.0.0
azure-mgmt-containerinstance==7.0.0
azure-mgmt-containerregistry==8.0.0b1
azure-mgmt-containerservice==15.1.0
azure-mgmt-core==1.2.2
azure-mgmt-cosmosdb==6.4.0
azure-mgmt-costmanagement==1.0.0
azure-mgmt-databricks==1.0.0b1
azure-mgmt-datafactory==1.1.0
azure-mgmt-datalake-store==1.0.0
azure-mgmt-dns==8.0.0b1
azure-mgmt-eventgrid==8.0.0
azure-mgmt-eventhub==8.0.0
azure-mgmt-frontdoor==1.0.0
azure-mgmt-hdinsight==7.0.0
azure-mgmt-iothub==1.0.0
azure-mgmt-keyvault==8.0.0
azure-mgmt-logic==9.0.0
azure-mgmt-managementgroups==1.0.0b1
azure-mgmt-monitor==2.0.0
azure-mgmt-msi==1.0.0
azure-mgmt-network==17.1.0
azure-mgmt-policyinsights==1.0.0
azure-mgmt-rdbms==8.1.0
azure-mgmt-redis==12.0.0
azure-mgmt-resource==16.1.0
azure-mgmt-resourcegraph==7.0.0
azure-mgmt-search==8.0.0
azure-mgmt-servicefabric==1.0.0
azure-mgmt-sql==1.0.0
azure-mgmt-storage==17.1.0
azure-mgmt-subscription==1.0.0
azure-mgmt-trafficmanager==0.51.0
azure-mgmt-web==2.0.0
azure-nspkg==3.0.2
azure-storage-blob==12.8.1
azure-storage-common==2.1.0
azure-storage-file==2.1.0
azure-storage-file-share==12.5.0
azure-storage-queue==12.1.6
boto3==1.17.102
botocore==1.20.102
c7n==0.9.13
cachetools==4.2.2
certifi==2021.5.30
cffi==1.14.5
chardet==4.0.0
click==7.1.2
cryptography==3.4.7
decorator==5.0.9
distlib==0.3.2
dogpile.cache==1.1.3
google-api-core==1.30.0
google-api-python-client==1.12.8
google-auth==1.32.0
google-auth-httplib2==0.1.0
google-cloud-core==1.7.1
google-cloud-logging==1.15.1
google-cloud-monitoring==0.34.0
google-cloud-storage==1.40.0
google-crc32c==1.1.2
google-resumable-media==1.3.1
googleapis-common-protos==1.53.0
httplib2==0.19.1
idna==2.10
importlib-metadata==4.6.0
iso8601==0.1.14
isodate==0.6.0
jmespath==0.10.0
jsonpatch==1.32
jsonpointer==2.1
jsonschema==3.2.0
keystoneauth1==4.3.1
kubernetes==10.0.1
mock==4.0.3
msal==1.12.0
msal-extensions==0.3.0
msrest==0.6.21
msrestazure==0.6.4
munch==2.5.0
netaddr==0.7.20
netifaces==0.11.0
oauthlib==3.1.1
openstacksdk==0.52.0
os-service-types==1.7.0
packaging==20.9
pbr==5.6.0
portalocker==1.7.1
protobuf==3.17.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
pyparsing==2.4.7
pyrsistent==0.18.0
python-dateutil==2.8.1
pytz==2021.1
pyyaml==5.4.1
ratelimiter==1.2.0.post0
requests==2.25.1
requests-oauthlib==1.3.0
requestsexceptions==1.4.0
retrying==1.3.3
rsa==4.7.2
s3transfer==0.4.2
setuptools==56.0.0
six==1.16.0
stevedore==3.3.0
tabulate==0.8.9
typing-extensions==3.10.0.0
tzlocal==2.1
uritemplate==3.0.1
urllib3==1.26.6
websocket-client==1.1.0
zipp==3.5.0
```


### Policy

```shell
- name: sns-encrypt-transit
  resource: aws.sns
  description: All SNS topics must be encrypted in transit
  filters:
    - not:
      - type: has-statement
        statements:
          - Action:
              - sns:GetTopicAttributes
              - sns:SetTopicAttributes
              - sns:AddPermission
              - sns:RemovePermission
              - sns:DeleteTopic
              - sns:Subscribe
              - sns:ListSubscriptionsByTopic
              - sns:Publish
              - sns:Receive
            Condition:
              Bool:
                aws:SecureTransport: 'false'
            Effect: Deny
            Principal: '*'
  actions:
    - type: modify-policy
      add-statements:
      - Action:
        - sns:GetTopicAttributes
        - sns:SetTopicAttributes
        - sns:AddPermission
        - sns:RemovePermission
        - sns:DeleteTopic
        - sns:Subscribe
        - sns:ListSubscriptionsByTopic
        - sns:Publish
        - sns:Receive
        Condition:
          Bool:
            aws:SecureTransport: 'false'
        Effect: Deny
        Principal: '*'
        Resource: '*'
        Sid: DenyNotEncryptedInTrans
    - type: notify
      action_desc: 'ACTION Taken: Custodian has added the policy statement to enable encryption in transit.'
      cc:
        - custodian_team@company.com
      from: no_reply@company.com
      template: default.html
      to:
        - team@company.com
        - resource-owner
      transport:
        type: sns
        topic: arn:aws:sns:us-east-1:0123456789:mailer
      violation_desc: The following SNS Topic(s) do not have encryption in transit enabled.
```


### Relevant log/traceback output

```shell
2021-10-08 12:58:33,582 - custodian.output - ERROR - Error while executing policy
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/c7n/policy.py", line 322, in run
    results = a.process(resources)
  File "/usr/local/lib/python3.7/dist-packages/c7n/resources/sns.py", line 330, in process
    new_policy, added = self.add_statements(new_policy)
  File "/usr/local/lib/python3.7/dist-packages/c7n/actions/policy.py", line 100, in add_statements
    current = {s['Sid']: s for s in policy_statements}
  File "/usr/local/lib/python3.7/dist-packages/c7n/actions/policy.py", line 100, in 
    current = {s['Sid']: s for s in policy_statements}
KeyError: 'Sid'
```


### Extra information or context

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `c7n/actions/policy.py`
Content:
```
1 # Copyright The Cloud Custodian Authors.
2 # SPDX-License-Identifier: Apache-2.0
3 
4 from .core import BaseAction
5 from c7n import utils
6 
7 
8 class RemovePolicyBase(BaseAction):
9 
10     schema = utils.type_schema(
11         'remove-statements',
12         required=['statement_ids'],
13         statement_ids={'oneOf': [
14             {'enum': ['matched', "*"]},
15             {'type': 'array', 'items': {'type': 'string'}}]})
16 
17     def process_policy(self, policy, resource, matched_key):
18         statements = policy.get('Statement', [])
19         resource_statements = resource.get(matched_key, ())
20 
21         return remove_statements(
22             self.data['statement_ids'], statements, resource_statements)
23 
24 
25 def remove_statements(match_ids, statements, matched=()):
26     found = []
27     for s in list(statements):
28         s_found = False
29         if match_ids == '*':
30             s_found = True
31         elif match_ids == 'matched':
32             if s in matched:
33                 s_found = True
34         elif 'Sid' in s and s['Sid'] in match_ids:
35             s_found = True
36         if s_found:
37             found.append(s)
38             statements.remove(s)
39     if not found:
40         return None, found
41     return statements, found
42 
43 
44 class ModifyPolicyBase(BaseAction):
45     """Action to modify resource IAM policy statements.
46 
47     Applies to all resources with embedded IAM Policies.
48 
49     :example:
50 
51     .. code-block:: yaml
52 
53            policies:
54               - name: sns-yank-cross-account
55                 resource: sns
56                 filters:
57                   - type: cross-account
58                 actions:
59                   - type: modify-policy
60                     add-statements: [{
61                         "Sid": "ReplaceWithMe",
62                         "Effect": "Allow",
63                         "Principal": "*",
64                         "Action": ["SNS:GetTopicAttributes"],
65                         "Resource": topic_arn,
66                             }]
67                     remove-statements: '*'
68     """
69 
70     schema_alias = True
71     schema = utils.type_schema(
72         'modify-policy',
73         **{
74             'add-statements': {
75                 'type': 'array',
76                 'items': {'$ref': '#/definitions/iam-statement'},
77             },
78             'remove-statements': {
79                 'type': ['array', 'string'],
80                 'oneOf': [
81                     {'enum': ['matched', '*']},
82                     {'type': 'array', 'items': {'type': 'string'}}
83                 ],
84             }
85         }
86     )
87 
88     def __init__(self, data=None, manager=None):
89         if manager is not None:
90             config_args = {
91                 'account_id': manager.config.account_id,
92                 'region': manager.config.region
93             }
94             self.data = utils.format_string_values(data, **config_args)
95         else:
96             self.data = utils.format_string_values(data)
97         self.manager = manager
98 
99     def add_statements(self, policy_statements):
100         current = {s['Sid']: s for s in policy_statements}
101         additional = {s['Sid']: s for s in self.data.get('add-statements', [])}
102         current.update(additional)
103         return list(current.values()), bool(additional)
104 
105     def remove_statements(self, policy_statements, resource, matched_key):
106         statement_ids = self.data.get('remove-statements', [])
107         found = []
108         if len(statement_ids) == 0:
109             return policy_statements, found
110         resource_statements = resource.get(matched_key, ())
111         return remove_statements(
112             statement_ids, policy_statements, resource_statements)
113
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/c7n/actions/policy.py b/c7n/actions/policy.py
--- a/c7n/actions/policy.py
+++ b/c7n/actions/policy.py
@@ -1,5 +1,6 @@
 # Copyright The Cloud Custodian Authors.
 # SPDX-License-Identifier: Apache-2.0
+import hashlib
 
 from .core import BaseAction
 from c7n import utils
@@ -41,6 +42,11 @@
     return statements, found
 
 
+def statement_id(s):
+    # for statements without a sid, use a checksum for identity
+    return hashlib.sha224(utils.dumps(s, indent=0).encode('utf8')).hexdigest()
+
+
 class ModifyPolicyBase(BaseAction):
     """Action to modify resource IAM policy statements.
 
@@ -97,8 +103,8 @@
         self.manager = manager
 
     def add_statements(self, policy_statements):
-        current = {s['Sid']: s for s in policy_statements}
-        additional = {s['Sid']: s for s in self.data.get('add-statements', [])}
+        current = {s.get('Sid', statement_id(s)): s for s in policy_statements}
+        additional = {s.get('Sid', statement_id(s)): s for s in self.data.get('add-statements', [])}
         current.update(additional)
         return list(current.values()), bool(additional)

{"golden_diff": "diff --git a/c7n/actions/policy.py b/c7n/actions/policy.py\n--- a/c7n/actions/policy.py\n+++ b/c7n/actions/policy.py\n@@ -1,5 +1,6 @@\n # Copyright The Cloud Custodian Authors.\n # SPDX-License-Identifier: Apache-2.0\n+import hashlib\n \n from .core import BaseAction\n from c7n import utils\n@@ -41,6 +42,11 @@\n     return statements, found\n \n \n+def statement_id(s):\n+    # for statements without a sid, use a checksum for identity\n+    return hashlib.sha224(utils.dumps(s, indent=0).encode('utf8')).hexdigest()\n+\n+\n class ModifyPolicyBase(BaseAction):\n     \"\"\"Action to modify resource IAM policy statements.\n \n@@ -97,8 +103,8 @@\n         self.manager = manager\n \n     def add_statements(self, policy_statements):\n-        current = {s['Sid']: s for s in policy_statements}\n-        additional = {s['Sid']: s for s in self.data.get('add-statements', [])}\n+        current = {s.get('Sid', statement_id(s)): s for s in policy_statements}\n+        additional = {s.get('Sid', statement_id(s)): s for s in self.data.get('add-statements', [])}\n         current.update(additional)\n         return list(current.values()), bool(additional)\n", "issue": "Key error when adding a policy statement to an SNS Topic\n### Describe the bug\n\nWhen adding/describing a statement to an SNS topic a key error is thrown when attempting to retrieve the `Sid` of the policy.\n\n### What did you expect to happen?\n\nA policy statement should be described and added without error\n\n### Cloud Provider\n\nAmazon Web Services (AWS)\n\n### Cloud Custodian version and dependency information\n\n```shell\nCustodian:   0.9.13\r\nPython:      3.8.11 (default, Jun 29 2021, 03:17:05) \r\n             [Clang 12.0.0 (clang-1200.0.32.29)]\r\nPlatform:    posix.uname_result(sysname='Darwin', nodename='f8ffc250c2ee', release='20.6.0', version='Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64', machine='x86_64')\r\nUsing venv:  True\r\nDocker: False\r\nInstalled: \r\n\r\nPyJWT==1.7.1\r\nPyYAML==5.4.1\r\nadal==1.2.7\r\nappdirs==1.4.4\r\napplicationinsights==0.11.10\r\napscheduler==3.7.0\r\nargcomplete==1.12.3\r\nattrs==21.2.0\r\nazure-common==1.1.27\r\nazure-core==1.15.0\r\nazure-cosmos==3.2.0\r\nazure-cosmosdb-nspkg==2.0.2\r\nazure-cosmosdb-table==1.0.6\r\nazure-functions==1.7.2\r\nazure-graphrbac==0.61.1\r\nazure-identity==1.6.0\r\nazure-keyvault==4.1.0\r\nazure-keyvault-certificates==4.3.0\r\nazure-keyvault-keys==4.4.0\r\nazure-keyvault-secrets==4.3.0\r\nazure-mgmt-apimanagement==1.0.0\r\nazure-mgmt-applicationinsights==1.0.0\r\nazure-mgmt-authorization==1.0.0\r\nazure-mgmt-batch==15.0.0\r\nazure-mgmt-cdn==10.0.0\r\nazure-mgmt-cognitiveservices==11.0.0\r\nazure-mgmt-compute==19.0.0\r\nazure-mgmt-containerinstance==7.0.0\r\nazure-mgmt-containerregistry==8.0.0b1\r\nazure-mgmt-containerservice==15.1.0\r\nazure-mgmt-core==1.2.2\r\nazure-mgmt-cosmosdb==6.4.0\r\nazure-mgmt-costmanagement==1.0.0\r\nazure-mgmt-databricks==1.0.0b1\r\nazure-mgmt-datafactory==1.1.0\r\nazure-mgmt-datalake-store==1.0.0\r\nazure-mgmt-dns==8.0.0b1\r\nazure-mgmt-eventgrid==8.0.0\r\nazure-mgmt-eventhub==8.0.0\r\nazure-mgmt-frontdoor==1.0.0\r\nazure-mgmt-hdinsight==7.0.0\r\nazure-mgmt-iothub==1.0.0\r\nazure-mgmt-keyvault==8.0.0\r\nazure-mgmt-logic==9.0.0\r\nazure-mgmt-managementgroups==1.0.0b1\r\nazure-mgmt-monitor==2.0.0\r\nazure-mgmt-msi==1.0.0\r\nazure-mgmt-network==17.1.0\r\nazure-mgmt-policyinsights==1.0.0\r\nazure-mgmt-rdbms==8.1.0\r\nazure-mgmt-redis==12.0.0\r\nazure-mgmt-resource==16.1.0\r\nazure-mgmt-resourcegraph==7.0.0\r\nazure-mgmt-search==8.0.0\r\nazure-mgmt-servicefabric==1.0.0\r\nazure-mgmt-sql==1.0.0\r\nazure-mgmt-storage==17.1.0\r\nazure-mgmt-subscription==1.0.0\r\nazure-mgmt-trafficmanager==0.51.0\r\nazure-mgmt-web==2.0.0\r\nazure-nspkg==3.0.2\r\nazure-storage-blob==12.8.1\r\nazure-storage-common==2.1.0\r\nazure-storage-file==2.1.0\r\nazure-storage-file-share==12.5.0\r\nazure-storage-queue==12.1.6\r\nboto3==1.17.102\r\nbotocore==1.20.102\r\nc7n==0.9.13\r\ncachetools==4.2.2\r\ncertifi==2021.5.30\r\ncffi==1.14.5\r\nchardet==4.0.0\r\nclick==7.1.2\r\ncryptography==3.4.7\r\ndecorator==5.0.9\r\ndistlib==0.3.2\r\ndogpile.cache==1.1.3\r\ngoogle-api-core==1.30.0\r\ngoogle-api-python-client==1.12.8\r\ngoogle-auth==1.32.0\r\ngoogle-auth-httplib2==0.1.0\r\ngoogle-cloud-core==1.7.1\r\ngoogle-cloud-logging==1.15.1\r\ngoogle-cloud-monitoring==0.34.0\r\ngoogle-cloud-storage==1.40.0\r\ngoogle-crc32c==1.1.2\r\ngoogle-resumable-media==1.3.1\r\ngoogleapis-common-protos==1.53.0\r\nhttplib2==0.19.1\r\nidna==2.10\r\nimportlib-metadata==4.6.0\r\niso8601==0.1.14\r\nisodate==0.6.0\r\njmespath==0.10.0\r\njsonpatch==1.32\r\njsonpointer==2.1\r\njsonschema==3.2.0\r\nkeystoneauth1==4.3.1\r\nkubernetes==10.0.1\r\nmock==4.0.3\r\nmsal==1.12.0\r\nmsal-extensions==0.3.0\r\nmsrest==0.6.21\r\nmsrestazure==0.6.4\r\nmunch==2.5.0\r\nnetaddr==0.7.20\r\nnetifaces==0.11.0\r\noauthlib==3.1.1\r\nopenstacksdk==0.52.0\r\nos-service-types==1.7.0\r\npackaging==20.9\r\npbr==5.6.0\r\nportalocker==1.7.1\r\nprotobuf==3.17.3\r\npyasn1==0.4.8\r\npyasn1-modules==0.2.8\r\npycparser==2.20\r\npyparsing==2.4.7\r\npyrsistent==0.18.0\r\npython-dateutil==2.8.1\r\npytz==2021.1\r\npyyaml==5.4.1\r\nratelimiter==1.2.0.post0\r\nrequests==2.25.1\r\nrequests-oauthlib==1.3.0\r\nrequestsexceptions==1.4.0\r\nretrying==1.3.3\r\nrsa==4.7.2\r\ns3transfer==0.4.2\r\nsetuptools==56.0.0\r\nsix==1.16.0\r\nstevedore==3.3.0\r\ntabulate==0.8.9\r\ntyping-extensions==3.10.0.0\r\ntzlocal==2.1\r\nuritemplate==3.0.1\r\nurllib3==1.26.6\r\nwebsocket-client==1.1.0\r\nzipp==3.5.0\n```\n\n\n### Policy\n\n```shell\n- name: sns-encrypt-transit\r\n  resource: aws.sns\r\n  description: All SNS topics must be encrypted in transit\r\n  filters:\r\n    - not:\r\n      - type: has-statement\r\n        statements:\r\n          - Action:\r\n              - sns:GetTopicAttributes\r\n              - sns:SetTopicAttributes\r\n              - sns:AddPermission\r\n              - sns:RemovePermission\r\n              - sns:DeleteTopic\r\n              - sns:Subscribe\r\n              - sns:ListSubscriptionsByTopic\r\n              - sns:Publish\r\n              - sns:Receive\r\n            Condition:\r\n              Bool:\r\n                aws:SecureTransport: 'false'\r\n            Effect: Deny\r\n            Principal: '*'\r\n  actions:\r\n    - type: modify-policy\r\n      add-statements:\r\n      - Action:\r\n        - sns:GetTopicAttributes\r\n        - sns:SetTopicAttributes\r\n        - sns:AddPermission\r\n        - sns:RemovePermission\r\n        - sns:DeleteTopic\r\n        - sns:Subscribe\r\n        - sns:ListSubscriptionsByTopic\r\n        - sns:Publish\r\n        - sns:Receive\r\n        Condition:\r\n          Bool:\r\n            aws:SecureTransport: 'false'\r\n        Effect: Deny\r\n        Principal: '*'\r\n        Resource: '*'\r\n        Sid: DenyNotEncryptedInTrans\r\n    - type: notify\r\n      action_desc: 'ACTION Taken: Custodian has added the policy statement to enable encryption in transit.'\r\n      cc:\r\n        - custodian_team@company.com\r\n      from: no_reply@company.com\r\n      template: default.html\r\n      to:\r\n        - team@company.com\r\n        - resource-owner\r\n      transport:\r\n        type: sns\r\n        topic: arn:aws:sns:us-east-1:0123456789:mailer\r\n      violation_desc: The following SNS Topic(s) do not have encryption in transit enabled.\n```\n\n\n### Relevant log/traceback output\n\n```shell\n2021-10-08 12:58:33,582 - custodian.output - ERROR - Error while executing policy\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.7/dist-packages/c7n/policy.py\", line 322, in run\r\n    results = a.process(resources)\r\n  File \"/usr/local/lib/python3.7/dist-packages/c7n/resources/sns.py\", line 330, in process\r\n    new_policy, added = self.add_statements(new_policy)\r\n  File \"/usr/local/lib/python3.7/dist-packages/c7n/actions/policy.py\", line 100, in add_statements\r\n    current = {s['Sid']: s for s in policy_statements}\r\n  File \"/usr/local/lib/python3.7/dist-packages/c7n/actions/policy.py\", line 100, in \r\n    current = {s['Sid']: s for s in policy_statements}\r\nKeyError: 'Sid'\n```\n\n\n### Extra information or context\n\n_No response_\n", "before_files": [{"content": "# Copyright The Cloud Custodian Authors.\n# SPDX-License-Identifier: Apache-2.0\n\nfrom .core import BaseAction\nfrom c7n import utils\n\n\nclass RemovePolicyBase(BaseAction):\n\n    schema = utils.type_schema(\n        'remove-statements',\n        required=['statement_ids'],\n        statement_ids={'oneOf': [\n            {'enum': ['matched', \"*\"]},\n            {'type': 'array', 'items': {'type': 'string'}}]})\n\n    def process_policy(self, policy, resource, matched_key):\n        statements = policy.get('Statement', [])\n        resource_statements = resource.get(matched_key, ())\n\n        return remove_statements(\n            self.data['statement_ids'], statements, resource_statements)\n\n\ndef remove_statements(match_ids, statements, matched=()):\n    found = []\n    for s in list(statements):\n        s_found = False\n        if match_ids == '*':\n            s_found = True\n        elif match_ids == 'matched':\n            if s in matched:\n                s_found = True\n        elif 'Sid' in s and s['Sid'] in match_ids:\n            s_found = True\n        if s_found:\n            found.append(s)\n            statements.remove(s)\n    if not found:\n        return None, found\n    return statements, found\n\n\nclass ModifyPolicyBase(BaseAction):\n    \"\"\"Action to modify resource IAM policy statements.\n\n    Applies to all resources with embedded IAM Policies.\n\n    :example:\n\n    .. code-block:: yaml\n\n           policies:\n              - name: sns-yank-cross-account\n                resource: sns\n                filters:\n                  - type: cross-account\n                actions:\n                  - type: modify-policy\n                    add-statements: [{\n                        \"Sid\": \"ReplaceWithMe\",\n                        \"Effect\": \"Allow\",\n                        \"Principal\": \"*\",\n                        \"Action\": [\"SNS:GetTopicAttributes\"],\n                        \"Resource\": topic_arn,\n                            }]\n                    remove-statements: '*'\n    \"\"\"\n\n    schema_alias = True\n    schema = utils.type_schema(\n        'modify-policy',\n        **{\n            'add-statements': {\n                'type': 'array',\n                'items': {'$ref': '#/definitions/iam-statement'},\n            },\n            'remove-statements': {\n                'type': ['array', 'string'],\n                'oneOf': [\n                    {'enum': ['matched', '*']},\n                    {'type': 'array', 'items': {'type': 'string'}}\n                ],\n            }\n        }\n    )\n\n    def __init__(self, data=None, manager=None):\n        if manager is not None:\n            config_args = {\n                'account_id': manager.config.account_id,\n                'region': manager.config.region\n            }\n            self.data = utils.format_string_values(data, **config_args)\n        else:\n            self.data = utils.format_string_values(data)\n        self.manager = manager\n\n    def add_statements(self, policy_statements):\n        current = {s['Sid']: s for s in policy_statements}\n        additional = {s['Sid']: s for s in self.data.get('add-statements', [])}\n        current.update(additional)\n        return list(current.values()), bool(additional)\n\n    def remove_statements(self, policy_statements, resource, matched_key):\n        statement_ids = self.data.get('remove-statements', [])\n        found = []\n        if len(statement_ids) == 0:\n            return policy_statements, found\n        resource_statements = resource.get(matched_key, ())\n        return remove_statements(\n            statement_ids, policy_statements, resource_statements)\n", "path": "c7n/actions/policy.py"}], "after_files": [{"content": "# Copyright The Cloud Custodian Authors.\n# SPDX-License-Identifier: Apache-2.0\nimport hashlib\n\nfrom .core import BaseAction\nfrom c7n import utils\n\n\nclass RemovePolicyBase(BaseAction):\n\n    schema = utils.type_schema(\n        'remove-statements',\n        required=['statement_ids'],\n        statement_ids={'oneOf': [\n            {'enum': ['matched', \"*\"]},\n            {'type': 'array', 'items': {'type': 'string'}}]})\n\n    def process_policy(self, policy, resource, matched_key):\n        statements = policy.get('Statement', [])\n        resource_statements = resource.get(matched_key, ())\n\n        return remove_statements(\n            self.data['statement_ids'], statements, resource_statements)\n\n\ndef remove_statements(match_ids, statements, matched=()):\n    found = []\n    for s in list(statements):\n        s_found = False\n        if match_ids == '*':\n            s_found = True\n        elif match_ids == 'matched':\n            if s in matched:\n                s_found = True\n        elif 'Sid' in s and s['Sid'] in match_ids:\n            s_found = True\n        if s_found:\n            found.append(s)\n            statements.remove(s)\n    if not found:\n        return None, found\n    return statements, found\n\n\ndef statement_id(s):\n    # for statements without a sid, use a checksum for identity\n    return hashlib.sha224(utils.dumps(s, indent=0).encode('utf8')).hexdigest()\n\n\nclass ModifyPolicyBase(BaseAction):\n    \"\"\"Action to modify resource IAM policy statements.\n\n    Applies to all resources with embedded IAM Policies.\n\n    :example:\n\n    .. code-block:: yaml\n\n           policies:\n              - name: sns-yank-cross-account\n                resource: sns\n                filters:\n                  - type: cross-account\n                actions:\n                  - type: modify-policy\n                    add-statements: [{\n                        \"Sid\": \"ReplaceWithMe\",\n                        \"Effect\": \"Allow\",\n                        \"Principal\": \"*\",\n                        \"Action\": [\"SNS:GetTopicAttributes\"],\n                        \"Resource\": topic_arn,\n                            }]\n                    remove-statements: '*'\n    \"\"\"\n\n    schema_alias = True\n    schema = utils.type_schema(\n        'modify-policy',\n        **{\n            'add-statements': {\n                'type': 'array',\n                'items': {'$ref': '#/definitions/iam-statement'},\n            },\n            'remove-statements': {\n                'type': ['array', 'string'],\n                'oneOf': [\n                    {'enum': ['matched', '*']},\n                    {'type': 'array', 'items': {'type': 'string'}}\n                ],\n            }\n        }\n    )\n\n    def __init__(self, data=None, manager=None):\n        if manager is not None:\n            config_args = {\n                'account_id': manager.config.account_id,\n                'region': manager.config.region\n            }\n            self.data = utils.format_string_values(data, **config_args)\n        else:\n            self.data = utils.format_string_values(data)\n        self.manager = manager\n\n    def add_statements(self, policy_statements):\n        current = {s.get('Sid', statement_id(s)): s for s in policy_statements}\n        additional = {s.get('Sid', statement_id(s)): s for s in self.data.get('add-statements', [])}\n        current.update(additional)\n        return list(current.values()), bool(additional)\n\n    def remove_statements(self, policy_statements, resource, matched_key):\n        statement_ids = self.data.get('remove-statements', [])\n        found = []\n        if len(statement_ids) == 0:\n            return policy_statements, found\n        resource_statements = resource.get(matched_key, ())\n        return remove_statements(\n            statement_ids, policy_statements, resource_statements)\n", "path": "c7n/actions/policy.py"}]}