prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  The s3 action "remove-statements" errors out when it encounters a bucket policy statement without a sid
  **Describe the bug**
  s3.remove-statements fails when a sid-less bucket policy statement is encountered
  
  You can see the key error in the traceback. Bucket policy statements do not require Sids and S3 omits the key from describeBucketPolicy response when it does not exist.
  
  **To Reproduce**
  Attempt to use remove-statements to remove a statement from a bucket with a sid-less statement (one example of which is the "aws-sam-cli-managed-default-samclisourcebucket-..." buckets created  by AWS SAM CLI.)
  
  **Expected behavior**
  I expected the statement which does not contain a SID to be iterated over as non-matching.
  
  **Background (please complete the following information):**
   - OS: AWS Lambda
   - Python Version:  Python 3.8
   - Custodian Version:  0.9.8
   - Tool Version: n/a
   - Cloud Provider: AWS
   - Policy: [please exclude any account/sensitive information]
  ```json
          {
            "statement_ids": [
              "denyAccessToBucket"
            ],
            "type": "remove-statements"
          }
  ```
   - Traceback: [if applicable, please exclude sensitive/account information]
   [ERROR] KeyError: 'Sid'
  Traceback (most recent call last):
    File "/var/task/custodian_policy.py", line 4, in run
      return handler.dispatch_event(event, context)
    File "/var/task/c7n/handler.py", line 165, in dispatch_event
      p.push(event, context)
    File "/var/task/c7n/policy.py", line 1140, in push
      return mode.run(event, lambda_ctx)
    File "/var/task/c7n/policy.py", line 853, in run
      resources = super(ConfigRuleMode, self).run(event, lambda_context)
    File "/var/task/c7n/policy.py", line 453, in run
      return self.run_resource_set(event, resources)
    File "/var/task/c7n/policy.py", line 483, in run_resource_set
      results = action.process(resources)
    File "/var/task/c7n/resources/s3.py", line 1272, in process
      results += filter(None, [f.result()])
    File "/var/lang/lib/python3.8/concurrent/futures/_base.py", line 432, in result
      return self.__get_result()
    File "/var/lang/lib/python3.8/concurrent/futures/_base.py", line 388, in __get_result
      raise self._exception
    File "/var/lang/lib/python3.8/concurrent/futures/thread.py", line 57, in run
      result = self.fn(*self.args, **self.kwargs)
    File "/var/task/c7n/resources/s3.py", line 1282, in process_bucket
      statements, found = self.process_policy(
    File "/var/task/c7n/actions/policy.py", line 21, in process_policy
      return remove_statements(
    File "/var/task/c7n/actions/policy.py", line 37, in remove_statements
      elif s['Sid'] in match_ids:
   - `custodian version --debug` output: n/a
  
  **Additional context**
  Add any other context about the problem here.
  
  
  --- 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 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()
  
  ```
  
golden_diff: diff --git a/c7n/actions/policy.py b/c7n/actions/policy.py
  --- a/c7n/actions/policy.py
  +++ b/c7n/actions/policy.py
  @@ -31,7 +31,7 @@
           elif match_ids == 'matched':
               if s in matched:
                   s_found = True
  -        elif s['Sid'] in match_ids:
  +        elif 'Sid' in s and s['Sid'] in match_ids:
               s_found = True
           if s_found:
               found.append(s)
  
verification_info: {"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@@ -31,7 +31,7 @@\n         elif match_ids == 'matched':\n             if s in matched:\n                 s_found = True\n-        elif s['Sid'] in match_ids:\n+        elif 'Sid' in s and s['Sid'] in match_ids:\n             s_found = True\n         if s_found:\n             found.append(s)\n", "issue": "The s3 action \"remove-statements\" errors out when it encounters a bucket policy statement without a sid\n**Describe the bug**\r\ns3.remove-statements fails when a sid-less bucket policy statement is encountered\r\n\r\nYou can see the key error in the traceback. Bucket policy statements do not require Sids and S3 omits the key from describeBucketPolicy response when it does not exist.\r\n\r\n**To Reproduce**\r\nAttempt to use remove-statements to remove a statement from a bucket with a sid-less statement (one example of which is the \"aws-sam-cli-managed-default-samclisourcebucket-...\" buckets created  by AWS SAM CLI.)\r\n\r\n**Expected behavior**\r\nI expected the statement which does not contain a SID to be iterated over as non-matching.\r\n\r\n**Background (please complete the following information):**\r\n - OS: AWS Lambda\r\n - Python Version:  Python 3.8\r\n - Custodian Version:  0.9.8\r\n - Tool Version: n/a\r\n - Cloud Provider: AWS\r\n - Policy: [please exclude any account/sensitive information]\r\n```json\r\n        {\r\n          \"statement_ids\": [\r\n            \"denyAccessToBucket\"\r\n          ],\r\n          \"type\": \"remove-statements\"\r\n        }\r\n```\r\n - Traceback: [if applicable, please exclude sensitive/account information]\r\n [ERROR] KeyError: 'Sid'\r\nTraceback (most recent call last):\r\n  File \"/var/task/custodian_policy.py\", line 4, in run\r\n    return handler.dispatch_event(event, context)\r\n  File \"/var/task/c7n/handler.py\", line 165, in dispatch_event\r\n    p.push(event, context)\r\n  File \"/var/task/c7n/policy.py\", line 1140, in push\r\n    return mode.run(event, lambda_ctx)\r\n  File \"/var/task/c7n/policy.py\", line 853, in run\r\n    resources = super(ConfigRuleMode, self).run(event, lambda_context)\r\n  File \"/var/task/c7n/policy.py\", line 453, in run\r\n    return self.run_resource_set(event, resources)\r\n  File \"/var/task/c7n/policy.py\", line 483, in run_resource_set\r\n    results = action.process(resources)\r\n  File \"/var/task/c7n/resources/s3.py\", line 1272, in process\r\n    results += filter(None, [f.result()])\r\n  File \"/var/lang/lib/python3.8/concurrent/futures/_base.py\", line 432, in result\r\n    return self.__get_result()\r\n  File \"/var/lang/lib/python3.8/concurrent/futures/_base.py\", line 388, in __get_result\r\n    raise self._exception\r\n  File \"/var/lang/lib/python3.8/concurrent/futures/thread.py\", line 57, in run\r\n    result = self.fn(*self.args, **self.kwargs)\r\n  File \"/var/task/c7n/resources/s3.py\", line 1282, in process_bucket\r\n    statements, found = self.process_policy(\r\n  File \"/var/task/c7n/actions/policy.py\", line 21, in process_policy\r\n    return remove_statements(\r\n  File \"/var/task/c7n/actions/policy.py\", line 37, in remove_statements\r\n    elif s['Sid'] in match_ids:\r\n - `custodian version --debug` output: n/a\r\n\r\n**Additional context**\r\nAdd any other context about the problem here.\r\n\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 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\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"}]}