We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Checkov throws an exception with serverless when it has only an Output resource block
Checkov throws an exception with serverless when it has only an Output resource block defined 

```
Traceback (most recent call last):
  File "C:\Users\xyz\AppData\Roaming\Python\Python39\Scripts\checkov.cmd", line 53, in <module>
    run()
  File "C:\Users\xyz\AppData\Roaming\Python\Python39\site-packages\checkov\main.py", line 97, in run
    scan_reports = runner_registry.run(root_folder=root_folder, external_checks_dir=external_checks_dir,
  File "C:\Users\xyz\AppData\Roaming\Python\Python39\site-packages\checkov\common\runners\runner_registry.py", line 34, in run
    scan_report = runner.run(root_folder, external_checks_dir=external_checks_dir, files=files,
  File "C:\Users\xyz\AppData\Roaming\Python\Python39\site-packages\checkov\serverless\runner.py", line 106, in run
    for resource_name, resource in cf_sub_template['Resources'].items():
KeyError: 'Resources'
```

**To Reproduce**
Here's a sample serverless.yml config 

```
service: my-function
provider:
  name: aws
  runtime: nodejs12.x
  region: eu-west-1

functions:
  my-function:
    name: my-function-${opt:stage}
    handler: index.handler

resources:
  Outputs:
    MyFunctionArn:
      Value: 
        Fn::GetAtt: [ MyDashFunctionLambdaFunction, Arn ]
      Export:
        Name: MyFunctionArn
```


**Screenshots**
If applicable, add screenshots to help explain your problem.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `checkov/serverless/runner.py`
Content:
```
1 import logging
2 import os
3 from typing import List, Dict, Tuple
4 
5 from checkov.cloudformation import cfn_utils
6 from checkov.cloudformation.context_parser import ContextParser as CfnContextParser
7 from checkov.common.parallelizer.parallel_runner import parallel_runner
8 from checkov.serverless.base_registry import EntityDetails
9 from checkov.serverless.parsers.context_parser import ContextParser as SlsContextParser
10 from checkov.cloudformation.checks.resource.registry import cfn_registry
11 from checkov.serverless.checks.complete.registry import complete_registry
12 from checkov.serverless.checks.custom.registry import custom_registry
13 from checkov.serverless.checks.function.registry import function_registry
14 from checkov.serverless.checks.layer.registry import layer_registry
15 from checkov.serverless.checks.package.registry import package_registry
16 from checkov.serverless.checks.plugin.registry import plugin_registry
17 from checkov.serverless.checks.provider.registry import provider_registry
18 from checkov.serverless.checks.service.registry import service_registry
19 from checkov.common.runners.base_runner import BaseRunner, filter_ignored_paths
20 from checkov.runner_filter import RunnerFilter
21 from checkov.common.output.record import Record
22 from checkov.common.output.report import Report
23 from checkov.serverless.parsers.parser import parse
24 from checkov.common.parsers.node import DictNode
25 from checkov.serverless.parsers.parser import CFN_RESOURCES_TOKEN
26 
27 SLS_FILE_MASK = os.getenv(
28     "CKV_SLS_FILE_MASK", "serverless.yml,serverless.yaml").split(",")
29 
30 MULTI_ITEM_SECTIONS = [
31     ("functions", function_registry),
32     ("layers", layer_registry)
33 ]
34 SINGLE_ITEM_SECTIONS = [
35     ("custom", custom_registry),
36     ("package", package_registry),
37     ("plugins", plugin_registry),
38     ("provider", provider_registry),
39     ("service", service_registry)
40 ]
41 
42 
43 class Runner(BaseRunner):
44     check_type = "serverless"
45 
46     def run(self, root_folder, external_checks_dir=None, files=None, runner_filter=RunnerFilter(), collect_skip_comments=True):
47         report = Report(self.check_type)
48         files_list = []
49         filepath_fn = None
50         if external_checks_dir:
51             for directory in external_checks_dir:
52                 function_registry.load_external_checks(directory)
53 
54         if files:
55             files_list = [file for file in files if os.path.basename(file) in SLS_FILE_MASK]
56 
57         if root_folder:
58             filepath_fn = lambda f: f'/{os.path.relpath(f, os.path.commonprefix((root_folder, f)))}'
59             for root, d_names, f_names in os.walk(root_folder):
60                 # Don't walk in to "node_modules" directories under the root folder. If –for some reason–
61                 # scanning one of these is desired, it can be directly specified.
62                 if "node_modules" in d_names:
63                     d_names.remove("node_modules")
64 
65                 filter_ignored_paths(root, d_names, runner_filter.excluded_paths)
66                 filter_ignored_paths(root, f_names, runner_filter.excluded_paths)
67                 for file in f_names:
68                     if file in SLS_FILE_MASK:
69                         full_path = os.path.join(root, file)
70                         if "/." not in full_path:
71                             # skip temp directories
72                             files_list.append(full_path)
73 
74         definitions, definitions_raw = get_files_definitions(files_list, filepath_fn)
75 
76         # Filter out empty files that have not been parsed successfully
77         definitions = {k: v for k, v in definitions.items() if v}
78         definitions_raw = {k: v for k, v in definitions_raw.items() if k in definitions.keys()}
79 
80         for sls_file, sls_file_data in definitions.items():
81 
82             # There are a few cases here. If -f was used, there could be a leading / because it's an absolute path,
83             # or there will be no leading slash; root_folder will always be none.
84             # If -d is used, root_folder will be the value given, and -f will start with a / (hardcoded above).
85             # The goal here is simply to get a valid path to the file (which sls_file does not always give).
86             if sls_file[0] == '/':
87                 path_to_convert = (root_folder + sls_file) if root_folder else sls_file
88             else:
89                 path_to_convert = (os.path.join(root_folder, sls_file)) if root_folder else sls_file
90 
91             file_abs_path = os.path.abspath(path_to_convert)
92 
93             if not isinstance(sls_file_data, DictNode):
94                 continue
95 
96             if CFN_RESOURCES_TOKEN in sls_file_data and isinstance(sls_file_data[CFN_RESOURCES_TOKEN], DictNode):
97                 cf_sub_template = sls_file_data[CFN_RESOURCES_TOKEN]
98                 if not cf_sub_template.get('Resources'):
99                     continue
100                 cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])
101                 logging.debug(f"Template Dump for {sls_file}: {sls_file_data}")
102                 cf_context_parser.evaluate_default_refs()
103                 for resource_name, resource in cf_sub_template['Resources'].items():
104                     if not isinstance(resource, DictNode):
105                         continue
106                     cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)
107                     if not cf_resource_id:
108                         # Not Type attribute for resource
109                         continue
110                     report.add_resource(f'{file_abs_path}:{cf_resource_id}')
111                     entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(
112                         resource)
113                     if entity_lines_range and entity_code_lines:
114                         skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
115                         # TODO - Variable Eval Message!
116                         variable_evaluations = {}
117 
118                         entity = {resource_name: resource}
119                         results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)
120                         tags = cfn_utils.get_resource_tags(entity, cfn_registry)
121                         for check, check_result in results.items():
122                             record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,
123                                             code_block=entity_code_lines, file_path=sls_file,
124                                             file_line_range=entity_lines_range,
125                                             resource=cf_resource_id, evaluations=variable_evaluations,
126                                             check_class=check.__class__.__module__, file_abs_path=file_abs_path,
127                                             entity_tags=tags)
128                             record.set_guideline(check.guideline)
129                             report.add_record(record=record)
130 
131             sls_context_parser = SlsContextParser(sls_file, sls_file_data, definitions_raw[sls_file])
132 
133             # Sub-sections that have multiple items under them
134             for token, registry in MULTI_ITEM_SECTIONS:
135                 template_items = sls_file_data.get(token)
136                 if not template_items or not isinstance(template_items, dict):
137                     continue
138                 for item_name, item_content in template_items.items():
139                     if not isinstance(item_content, DictNode):
140                         continue
141                     entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)
142                     if entity_lines_range and entity_code_lines:
143                         skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
144                         variable_evaluations = {}
145                         if token == "functions": #nosec
146                             # "Enriching" copies things like "environment" and "stackTags" down into the
147                             # function data from the provider block since logically that's what serverless
148                             # does. This allows checks to see what the complete data would be.
149                             sls_context_parser.enrich_function_with_provider(item_name)
150                         entity = EntityDetails(sls_context_parser.provider_type, item_content)
151                         results = registry.scan(sls_file, entity, skipped_checks, runner_filter)
152                         tags = cfn_utils.get_resource_tags(entity, registry)
153                         for check, check_result in results.items():
154                             record = Record(check_id=check.id, check_name=check.name, check_result=check_result,
155                                             code_block=entity_code_lines, file_path=sls_file,
156                                             file_line_range=entity_lines_range,
157                                             resource=item_name, evaluations=variable_evaluations,
158                                             check_class=check.__class__.__module__, file_abs_path=file_abs_path,
159                                             entity_tags=tags)
160                             record.set_guideline(check.guideline)
161                             report.add_record(record=record)
162             # Sub-sections that are a single item
163             for token, registry in SINGLE_ITEM_SECTIONS:
164                 item_content = sls_file_data.get(token)
165                 if not item_content:
166                     continue
167                 entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)
168                 if not entity_lines_range:
169                     entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)
170 
171                 skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
172                 variable_evaluations = {}
173                 entity = EntityDetails(sls_context_parser.provider_type, item_content)
174                 results = registry.scan(sls_file, entity, skipped_checks, runner_filter)
175                 tags = cfn_utils.get_resource_tags(entity, registry)
176                 for check, check_result in results.items():
177                     record = Record(check_id=check.id, check_name=check.name, check_result=check_result,
178                                     code_block=entity_code_lines, file_path=sls_file,
179                                     file_line_range=entity_lines_range,
180                                     resource=token, evaluations=variable_evaluations,
181                                     check_class=check.__class__.__module__, file_abs_path=file_abs_path,
182                                     entity_tags=tags)
183                     record.set_guideline(check.guideline)
184                     report.add_record(record=record)
185 
186             # "Complete" checks
187             # NOTE: Ignore code content, no point in showing (could be long)
188             entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)
189             if entity_lines_range:
190                 skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
191                 variable_evaluations = {}
192                 entity = EntityDetails(sls_context_parser.provider_type, sls_file_data)
193                 results = complete_registry.scan(sls_file, entity, skipped_checks, runner_filter)
194                 tags = cfn_utils.get_resource_tags(entity, complete_registry)
195                 for check, check_result in results.items():
196                     record = Record(check_id=check.id, check_name=check.name, check_result=check_result,
197                                     code_block=[],              # Don't show, could be large
198                                     file_path=sls_file,
199                                     file_line_range=entity_lines_range,
200                                     resource="complete",        # Weird, not sure what to put where
201                                     evaluations=variable_evaluations,
202                                     check_class=check.__class__.__module__, file_abs_path=file_abs_path,
203                                     entity_tags=tags)
204                     record.set_guideline(check.guideline)
205                     report.add_record(record=record)
206 
207         return report
208 
209 
210 def get_files_definitions(files: List[str], filepath_fn=None) \
211         -> Tuple[Dict[str, DictNode], Dict[str, List[Tuple[int, str]]]]:
212     results = parallel_runner.run_function(lambda f: (f, parse(f)), files)
213     definitions = {}
214     definitions_raw = {}
215     for file, result in results:
216         if result:
217             path = filepath_fn(file) if filepath_fn else file
218             definitions[path], definitions_raw[path] = result
219 
220     return definitions, definitions_raw
221
```
--- 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/checkov/serverless/runner.py b/checkov/serverless/runner.py
--- a/checkov/serverless/runner.py
+++ b/checkov/serverless/runner.py
@@ -95,38 +95,37 @@
 
             if CFN_RESOURCES_TOKEN in sls_file_data and isinstance(sls_file_data[CFN_RESOURCES_TOKEN], DictNode):
                 cf_sub_template = sls_file_data[CFN_RESOURCES_TOKEN]
-                if not cf_sub_template.get('Resources'):
-                    continue
-                cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])
-                logging.debug(f"Template Dump for {sls_file}: {sls_file_data}")
-                cf_context_parser.evaluate_default_refs()
-                for resource_name, resource in cf_sub_template['Resources'].items():
-                    if not isinstance(resource, DictNode):
-                        continue
-                    cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)
-                    if not cf_resource_id:
-                        # Not Type attribute for resource
-                        continue
-                    report.add_resource(f'{file_abs_path}:{cf_resource_id}')
-                    entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(
-                        resource)
-                    if entity_lines_range and entity_code_lines:
-                        skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
-                        # TODO - Variable Eval Message!
-                        variable_evaluations = {}
-
-                        entity = {resource_name: resource}
-                        results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)
-                        tags = cfn_utils.get_resource_tags(entity, cfn_registry)
-                        for check, check_result in results.items():
-                            record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,
-                                            code_block=entity_code_lines, file_path=sls_file,
-                                            file_line_range=entity_lines_range,
-                                            resource=cf_resource_id, evaluations=variable_evaluations,
-                                            check_class=check.__class__.__module__, file_abs_path=file_abs_path,
-                                            entity_tags=tags)
-                            record.set_guideline(check.guideline)
-                            report.add_record(record=record)
+                if cf_sub_template.get("Resources"):
+                    cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])
+                    logging.debug(f"Template Dump for {sls_file}: {sls_file_data}")
+                    cf_context_parser.evaluate_default_refs()
+                    for resource_name, resource in cf_sub_template['Resources'].items():
+                        if not isinstance(resource, DictNode):
+                            continue
+                        cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)
+                        if not cf_resource_id:
+                            # Not Type attribute for resource
+                            continue
+                        report.add_resource(f'{file_abs_path}:{cf_resource_id}')
+                        entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(
+                            resource)
+                        if entity_lines_range and entity_code_lines:
+                            skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)
+                            # TODO - Variable Eval Message!
+                            variable_evaluations = {}
+
+                            entity = {resource_name: resource}
+                            results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)
+                            tags = cfn_utils.get_resource_tags(entity, cfn_registry)
+                            for check, check_result in results.items():
+                                record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,
+                                                code_block=entity_code_lines, file_path=sls_file,
+                                                file_line_range=entity_lines_range,
+                                                resource=cf_resource_id, evaluations=variable_evaluations,
+                                                check_class=check.__class__.__module__, file_abs_path=file_abs_path,
+                                                entity_tags=tags)
+                                record.set_guideline(check.guideline)
+                                report.add_record(record=record)
 
             sls_context_parser = SlsContextParser(sls_file, sls_file_data, definitions_raw[sls_file])

{"golden_diff": "diff --git a/checkov/serverless/runner.py b/checkov/serverless/runner.py\n--- a/checkov/serverless/runner.py\n+++ b/checkov/serverless/runner.py\n@@ -95,38 +95,37 @@\n \n             if CFN_RESOURCES_TOKEN in sls_file_data and isinstance(sls_file_data[CFN_RESOURCES_TOKEN], DictNode):\n                 cf_sub_template = sls_file_data[CFN_RESOURCES_TOKEN]\n-                if not cf_sub_template.get('Resources'):\n-                    continue\n-                cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])\n-                logging.debug(f\"Template Dump for {sls_file}: {sls_file_data}\")\n-                cf_context_parser.evaluate_default_refs()\n-                for resource_name, resource in cf_sub_template['Resources'].items():\n-                    if not isinstance(resource, DictNode):\n-                        continue\n-                    cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)\n-                    if not cf_resource_id:\n-                        # Not Type attribute for resource\n-                        continue\n-                    report.add_resource(f'{file_abs_path}:{cf_resource_id}')\n-                    entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(\n-                        resource)\n-                    if entity_lines_range and entity_code_lines:\n-                        skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n-                        # TODO - Variable Eval Message!\n-                        variable_evaluations = {}\n-\n-                        entity = {resource_name: resource}\n-                        results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n-                        tags = cfn_utils.get_resource_tags(entity, cfn_registry)\n-                        for check, check_result in results.items():\n-                            record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,\n-                                            code_block=entity_code_lines, file_path=sls_file,\n-                                            file_line_range=entity_lines_range,\n-                                            resource=cf_resource_id, evaluations=variable_evaluations,\n-                                            check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n-                                            entity_tags=tags)\n-                            record.set_guideline(check.guideline)\n-                            report.add_record(record=record)\n+                if cf_sub_template.get(\"Resources\"):\n+                    cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])\n+                    logging.debug(f\"Template Dump for {sls_file}: {sls_file_data}\")\n+                    cf_context_parser.evaluate_default_refs()\n+                    for resource_name, resource in cf_sub_template['Resources'].items():\n+                        if not isinstance(resource, DictNode):\n+                            continue\n+                        cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)\n+                        if not cf_resource_id:\n+                            # Not Type attribute for resource\n+                            continue\n+                        report.add_resource(f'{file_abs_path}:{cf_resource_id}')\n+                        entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(\n+                            resource)\n+                        if entity_lines_range and entity_code_lines:\n+                            skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n+                            # TODO - Variable Eval Message!\n+                            variable_evaluations = {}\n+\n+                            entity = {resource_name: resource}\n+                            results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n+                            tags = cfn_utils.get_resource_tags(entity, cfn_registry)\n+                            for check, check_result in results.items():\n+                                record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,\n+                                                code_block=entity_code_lines, file_path=sls_file,\n+                                                file_line_range=entity_lines_range,\n+                                                resource=cf_resource_id, evaluations=variable_evaluations,\n+                                                check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n+                                                entity_tags=tags)\n+                                record.set_guideline(check.guideline)\n+                                report.add_record(record=record)\n \n             sls_context_parser = SlsContextParser(sls_file, sls_file_data, definitions_raw[sls_file])\n", "issue": "Checkov throws an exception with serverless when it has only an Output resource block\nCheckov throws an exception with serverless when it has only an Output resource block defined \r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\xyz\\AppData\\Roaming\\Python\\Python39\\Scripts\\checkov.cmd\", line 53, in <module>\r\n    run()\r\n  File \"C:\\Users\\xyz\\AppData\\Roaming\\Python\\Python39\\site-packages\\checkov\\main.py\", line 97, in run\r\n    scan_reports = runner_registry.run(root_folder=root_folder, external_checks_dir=external_checks_dir,\r\n  File \"C:\\Users\\xyz\\AppData\\Roaming\\Python\\Python39\\site-packages\\checkov\\common\\runners\\runner_registry.py\", line 34, in run\r\n    scan_report = runner.run(root_folder, external_checks_dir=external_checks_dir, files=files,\r\n  File \"C:\\Users\\xyz\\AppData\\Roaming\\Python\\Python39\\site-packages\\checkov\\serverless\\runner.py\", line 106, in run\r\n    for resource_name, resource in cf_sub_template['Resources'].items():\r\nKeyError: 'Resources'\r\n```\r\n\r\n**To Reproduce**\r\nHere's a sample serverless.yml config \r\n\r\n```\r\nservice: my-function\r\nprovider:\r\n  name: aws\r\n  runtime: nodejs12.x\r\n  region: eu-west-1\r\n\r\nfunctions:\r\n  my-function:\r\n    name: my-function-${opt:stage}\r\n    handler: index.handler\r\n\r\nresources:\r\n  Outputs:\r\n    MyFunctionArn:\r\n      Value: \r\n        Fn::GetAtt: [ MyDashFunctionLambdaFunction, Arn ]\r\n      Export:\r\n        Name: MyFunctionArn\r\n```\r\n\r\n\r\n**Screenshots**\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n\n", "before_files": [{"content": "import logging\nimport os\nfrom typing import List, Dict, Tuple\n\nfrom checkov.cloudformation import cfn_utils\nfrom checkov.cloudformation.context_parser import ContextParser as CfnContextParser\nfrom checkov.common.parallelizer.parallel_runner import parallel_runner\nfrom checkov.serverless.base_registry import EntityDetails\nfrom checkov.serverless.parsers.context_parser import ContextParser as SlsContextParser\nfrom checkov.cloudformation.checks.resource.registry import cfn_registry\nfrom checkov.serverless.checks.complete.registry import complete_registry\nfrom checkov.serverless.checks.custom.registry import custom_registry\nfrom checkov.serverless.checks.function.registry import function_registry\nfrom checkov.serverless.checks.layer.registry import layer_registry\nfrom checkov.serverless.checks.package.registry import package_registry\nfrom checkov.serverless.checks.plugin.registry import plugin_registry\nfrom checkov.serverless.checks.provider.registry import provider_registry\nfrom checkov.serverless.checks.service.registry import service_registry\nfrom checkov.common.runners.base_runner import BaseRunner, filter_ignored_paths\nfrom checkov.runner_filter import RunnerFilter\nfrom checkov.common.output.record import Record\nfrom checkov.common.output.report import Report\nfrom checkov.serverless.parsers.parser import parse\nfrom checkov.common.parsers.node import DictNode\nfrom checkov.serverless.parsers.parser import CFN_RESOURCES_TOKEN\n\nSLS_FILE_MASK = os.getenv(\n    \"CKV_SLS_FILE_MASK\", \"serverless.yml,serverless.yaml\").split(\",\")\n\nMULTI_ITEM_SECTIONS = [\n    (\"functions\", function_registry),\n    (\"layers\", layer_registry)\n]\nSINGLE_ITEM_SECTIONS = [\n    (\"custom\", custom_registry),\n    (\"package\", package_registry),\n    (\"plugins\", plugin_registry),\n    (\"provider\", provider_registry),\n    (\"service\", service_registry)\n]\n\n\nclass Runner(BaseRunner):\n    check_type = \"serverless\"\n\n    def run(self, root_folder, external_checks_dir=None, files=None, runner_filter=RunnerFilter(), collect_skip_comments=True):\n        report = Report(self.check_type)\n        files_list = []\n        filepath_fn = None\n        if external_checks_dir:\n            for directory in external_checks_dir:\n                function_registry.load_external_checks(directory)\n\n        if files:\n            files_list = [file for file in files if os.path.basename(file) in SLS_FILE_MASK]\n\n        if root_folder:\n            filepath_fn = lambda f: f'/{os.path.relpath(f, os.path.commonprefix((root_folder, f)))}'\n            for root, d_names, f_names in os.walk(root_folder):\n                # Don't walk in to \"node_modules\" directories under the root folder. If \u2013for some reason\u2013\n                # scanning one of these is desired, it can be directly specified.\n                if \"node_modules\" in d_names:\n                    d_names.remove(\"node_modules\")\n\n                filter_ignored_paths(root, d_names, runner_filter.excluded_paths)\n                filter_ignored_paths(root, f_names, runner_filter.excluded_paths)\n                for file in f_names:\n                    if file in SLS_FILE_MASK:\n                        full_path = os.path.join(root, file)\n                        if \"/.\" not in full_path:\n                            # skip temp directories\n                            files_list.append(full_path)\n\n        definitions, definitions_raw = get_files_definitions(files_list, filepath_fn)\n\n        # Filter out empty files that have not been parsed successfully\n        definitions = {k: v for k, v in definitions.items() if v}\n        definitions_raw = {k: v for k, v in definitions_raw.items() if k in definitions.keys()}\n\n        for sls_file, sls_file_data in definitions.items():\n\n            # There are a few cases here. If -f was used, there could be a leading / because it's an absolute path,\n            # or there will be no leading slash; root_folder will always be none.\n            # If -d is used, root_folder will be the value given, and -f will start with a / (hardcoded above).\n            # The goal here is simply to get a valid path to the file (which sls_file does not always give).\n            if sls_file[0] == '/':\n                path_to_convert = (root_folder + sls_file) if root_folder else sls_file\n            else:\n                path_to_convert = (os.path.join(root_folder, sls_file)) if root_folder else sls_file\n\n            file_abs_path = os.path.abspath(path_to_convert)\n\n            if not isinstance(sls_file_data, DictNode):\n                continue\n\n            if CFN_RESOURCES_TOKEN in sls_file_data and isinstance(sls_file_data[CFN_RESOURCES_TOKEN], DictNode):\n                cf_sub_template = sls_file_data[CFN_RESOURCES_TOKEN]\n                if not cf_sub_template.get('Resources'):\n                    continue\n                cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])\n                logging.debug(f\"Template Dump for {sls_file}: {sls_file_data}\")\n                cf_context_parser.evaluate_default_refs()\n                for resource_name, resource in cf_sub_template['Resources'].items():\n                    if not isinstance(resource, DictNode):\n                        continue\n                    cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)\n                    if not cf_resource_id:\n                        # Not Type attribute for resource\n                        continue\n                    report.add_resource(f'{file_abs_path}:{cf_resource_id}')\n                    entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(\n                        resource)\n                    if entity_lines_range and entity_code_lines:\n                        skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                        # TODO - Variable Eval Message!\n                        variable_evaluations = {}\n\n                        entity = {resource_name: resource}\n                        results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                        tags = cfn_utils.get_resource_tags(entity, cfn_registry)\n                        for check, check_result in results.items():\n                            record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,\n                                            code_block=entity_code_lines, file_path=sls_file,\n                                            file_line_range=entity_lines_range,\n                                            resource=cf_resource_id, evaluations=variable_evaluations,\n                                            check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                            entity_tags=tags)\n                            record.set_guideline(check.guideline)\n                            report.add_record(record=record)\n\n            sls_context_parser = SlsContextParser(sls_file, sls_file_data, definitions_raw[sls_file])\n\n            # Sub-sections that have multiple items under them\n            for token, registry in MULTI_ITEM_SECTIONS:\n                template_items = sls_file_data.get(token)\n                if not template_items or not isinstance(template_items, dict):\n                    continue\n                for item_name, item_content in template_items.items():\n                    if not isinstance(item_content, DictNode):\n                        continue\n                    entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)\n                    if entity_lines_range and entity_code_lines:\n                        skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                        variable_evaluations = {}\n                        if token == \"functions\": #nosec\n                            # \"Enriching\" copies things like \"environment\" and \"stackTags\" down into the\n                            # function data from the provider block since logically that's what serverless\n                            # does. This allows checks to see what the complete data would be.\n                            sls_context_parser.enrich_function_with_provider(item_name)\n                        entity = EntityDetails(sls_context_parser.provider_type, item_content)\n                        results = registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                        tags = cfn_utils.get_resource_tags(entity, registry)\n                        for check, check_result in results.items():\n                            record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                            code_block=entity_code_lines, file_path=sls_file,\n                                            file_line_range=entity_lines_range,\n                                            resource=item_name, evaluations=variable_evaluations,\n                                            check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                            entity_tags=tags)\n                            record.set_guideline(check.guideline)\n                            report.add_record(record=record)\n            # Sub-sections that are a single item\n            for token, registry in SINGLE_ITEM_SECTIONS:\n                item_content = sls_file_data.get(token)\n                if not item_content:\n                    continue\n                entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)\n                if not entity_lines_range:\n                    entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)\n\n                skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                variable_evaluations = {}\n                entity = EntityDetails(sls_context_parser.provider_type, item_content)\n                results = registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                tags = cfn_utils.get_resource_tags(entity, registry)\n                for check, check_result in results.items():\n                    record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                    code_block=entity_code_lines, file_path=sls_file,\n                                    file_line_range=entity_lines_range,\n                                    resource=token, evaluations=variable_evaluations,\n                                    check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                    entity_tags=tags)\n                    record.set_guideline(check.guideline)\n                    report.add_record(record=record)\n\n            # \"Complete\" checks\n            # NOTE: Ignore code content, no point in showing (could be long)\n            entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)\n            if entity_lines_range:\n                skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                variable_evaluations = {}\n                entity = EntityDetails(sls_context_parser.provider_type, sls_file_data)\n                results = complete_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                tags = cfn_utils.get_resource_tags(entity, complete_registry)\n                for check, check_result in results.items():\n                    record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                    code_block=[],              # Don't show, could be large\n                                    file_path=sls_file,\n                                    file_line_range=entity_lines_range,\n                                    resource=\"complete\",        # Weird, not sure what to put where\n                                    evaluations=variable_evaluations,\n                                    check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                    entity_tags=tags)\n                    record.set_guideline(check.guideline)\n                    report.add_record(record=record)\n\n        return report\n\n\ndef get_files_definitions(files: List[str], filepath_fn=None) \\\n        -> Tuple[Dict[str, DictNode], Dict[str, List[Tuple[int, str]]]]:\n    results = parallel_runner.run_function(lambda f: (f, parse(f)), files)\n    definitions = {}\n    definitions_raw = {}\n    for file, result in results:\n        if result:\n            path = filepath_fn(file) if filepath_fn else file\n            definitions[path], definitions_raw[path] = result\n\n    return definitions, definitions_raw\n", "path": "checkov/serverless/runner.py"}], "after_files": [{"content": "import logging\nimport os\nfrom typing import List, Dict, Tuple\n\nfrom checkov.cloudformation import cfn_utils\nfrom checkov.cloudformation.context_parser import ContextParser as CfnContextParser\nfrom checkov.common.parallelizer.parallel_runner import parallel_runner\nfrom checkov.serverless.base_registry import EntityDetails\nfrom checkov.serverless.parsers.context_parser import ContextParser as SlsContextParser\nfrom checkov.cloudformation.checks.resource.registry import cfn_registry\nfrom checkov.serverless.checks.complete.registry import complete_registry\nfrom checkov.serverless.checks.custom.registry import custom_registry\nfrom checkov.serverless.checks.function.registry import function_registry\nfrom checkov.serverless.checks.layer.registry import layer_registry\nfrom checkov.serverless.checks.package.registry import package_registry\nfrom checkov.serverless.checks.plugin.registry import plugin_registry\nfrom checkov.serverless.checks.provider.registry import provider_registry\nfrom checkov.serverless.checks.service.registry import service_registry\nfrom checkov.common.runners.base_runner import BaseRunner, filter_ignored_paths\nfrom checkov.runner_filter import RunnerFilter\nfrom checkov.common.output.record import Record\nfrom checkov.common.output.report import Report\nfrom checkov.serverless.parsers.parser import parse\nfrom checkov.common.parsers.node import DictNode\nfrom checkov.serverless.parsers.parser import CFN_RESOURCES_TOKEN\n\nSLS_FILE_MASK = os.getenv(\n    \"CKV_SLS_FILE_MASK\", \"serverless.yml,serverless.yaml\").split(\",\")\n\nMULTI_ITEM_SECTIONS = [\n    (\"functions\", function_registry),\n    (\"layers\", layer_registry)\n]\nSINGLE_ITEM_SECTIONS = [\n    (\"custom\", custom_registry),\n    (\"package\", package_registry),\n    (\"plugins\", plugin_registry),\n    (\"provider\", provider_registry),\n    (\"service\", service_registry)\n]\n\n\nclass Runner(BaseRunner):\n    check_type = \"serverless\"\n\n    def run(self, root_folder, external_checks_dir=None, files=None, runner_filter=RunnerFilter(), collect_skip_comments=True):\n        report = Report(self.check_type)\n        files_list = []\n        filepath_fn = None\n        if external_checks_dir:\n            for directory in external_checks_dir:\n                function_registry.load_external_checks(directory)\n\n        if files:\n            files_list = [file for file in files if os.path.basename(file) in SLS_FILE_MASK]\n\n        if root_folder:\n            filepath_fn = lambda f: f'/{os.path.relpath(f, os.path.commonprefix((root_folder, f)))}'\n            for root, d_names, f_names in os.walk(root_folder):\n                # Don't walk in to \"node_modules\" directories under the root folder. If \u2013for some reason\u2013\n                # scanning one of these is desired, it can be directly specified.\n                if \"node_modules\" in d_names:\n                    d_names.remove(\"node_modules\")\n\n                filter_ignored_paths(root, d_names, runner_filter.excluded_paths)\n                filter_ignored_paths(root, f_names, runner_filter.excluded_paths)\n                for file in f_names:\n                    if file in SLS_FILE_MASK:\n                        full_path = os.path.join(root, file)\n                        if \"/.\" not in full_path:\n                            # skip temp directories\n                            files_list.append(full_path)\n\n        definitions, definitions_raw = get_files_definitions(files_list, filepath_fn)\n\n        # Filter out empty files that have not been parsed successfully\n        definitions = {k: v for k, v in definitions.items() if v}\n        definitions_raw = {k: v for k, v in definitions_raw.items() if k in definitions.keys()}\n\n        for sls_file, sls_file_data in definitions.items():\n\n            # There are a few cases here. If -f was used, there could be a leading / because it's an absolute path,\n            # or there will be no leading slash; root_folder will always be none.\n            # If -d is used, root_folder will be the value given, and -f will start with a / (hardcoded above).\n            # The goal here is simply to get a valid path to the file (which sls_file does not always give).\n            if sls_file[0] == '/':\n                path_to_convert = (root_folder + sls_file) if root_folder else sls_file\n            else:\n                path_to_convert = (os.path.join(root_folder, sls_file)) if root_folder else sls_file\n\n            file_abs_path = os.path.abspath(path_to_convert)\n\n            if not isinstance(sls_file_data, DictNode):\n                continue\n\n            if CFN_RESOURCES_TOKEN in sls_file_data and isinstance(sls_file_data[CFN_RESOURCES_TOKEN], DictNode):\n                cf_sub_template = sls_file_data[CFN_RESOURCES_TOKEN]\n                if cf_sub_template.get(\"Resources\"):\n                    cf_context_parser = CfnContextParser(sls_file, cf_sub_template, definitions_raw[sls_file])\n                    logging.debug(f\"Template Dump for {sls_file}: {sls_file_data}\")\n                    cf_context_parser.evaluate_default_refs()\n                    for resource_name, resource in cf_sub_template['Resources'].items():\n                        if not isinstance(resource, DictNode):\n                            continue\n                        cf_resource_id = cf_context_parser.extract_cf_resource_id(resource, resource_name)\n                        if not cf_resource_id:\n                            # Not Type attribute for resource\n                            continue\n                        report.add_resource(f'{file_abs_path}:{cf_resource_id}')\n                        entity_lines_range, entity_code_lines = cf_context_parser.extract_cf_resource_code_lines(\n                            resource)\n                        if entity_lines_range and entity_code_lines:\n                            skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                            # TODO - Variable Eval Message!\n                            variable_evaluations = {}\n\n                            entity = {resource_name: resource}\n                            results = cfn_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                            tags = cfn_utils.get_resource_tags(entity, cfn_registry)\n                            for check, check_result in results.items():\n                                record = Record(check_id=check.id, bc_check_id=check.bc_id, check_name=check.name, check_result=check_result,\n                                                code_block=entity_code_lines, file_path=sls_file,\n                                                file_line_range=entity_lines_range,\n                                                resource=cf_resource_id, evaluations=variable_evaluations,\n                                                check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                                entity_tags=tags)\n                                record.set_guideline(check.guideline)\n                                report.add_record(record=record)\n\n            sls_context_parser = SlsContextParser(sls_file, sls_file_data, definitions_raw[sls_file])\n\n            # Sub-sections that have multiple items under them\n            for token, registry in MULTI_ITEM_SECTIONS:\n                template_items = sls_file_data.get(token)\n                if not template_items or not isinstance(template_items, dict):\n                    continue\n                for item_name, item_content in template_items.items():\n                    if not isinstance(item_content, DictNode):\n                        continue\n                    entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)\n                    if entity_lines_range and entity_code_lines:\n                        skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                        variable_evaluations = {}\n                        if token == \"functions\": #nosec\n                            # \"Enriching\" copies things like \"environment\" and \"stackTags\" down into the\n                            # function data from the provider block since logically that's what serverless\n                            # does. This allows checks to see what the complete data would be.\n                            sls_context_parser.enrich_function_with_provider(item_name)\n                        entity = EntityDetails(sls_context_parser.provider_type, item_content)\n                        results = registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                        tags = cfn_utils.get_resource_tags(entity, registry)\n                        for check, check_result in results.items():\n                            record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                            code_block=entity_code_lines, file_path=sls_file,\n                                            file_line_range=entity_lines_range,\n                                            resource=item_name, evaluations=variable_evaluations,\n                                            check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                            entity_tags=tags)\n                            record.set_guideline(check.guideline)\n                            report.add_record(record=record)\n            # Sub-sections that are a single item\n            for token, registry in SINGLE_ITEM_SECTIONS:\n                item_content = sls_file_data.get(token)\n                if not item_content:\n                    continue\n                entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(item_content)\n                if not entity_lines_range:\n                    entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)\n\n                skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                variable_evaluations = {}\n                entity = EntityDetails(sls_context_parser.provider_type, item_content)\n                results = registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                tags = cfn_utils.get_resource_tags(entity, registry)\n                for check, check_result in results.items():\n                    record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                    code_block=entity_code_lines, file_path=sls_file,\n                                    file_line_range=entity_lines_range,\n                                    resource=token, evaluations=variable_evaluations,\n                                    check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                    entity_tags=tags)\n                    record.set_guideline(check.guideline)\n                    report.add_record(record=record)\n\n            # \"Complete\" checks\n            # NOTE: Ignore code content, no point in showing (could be long)\n            entity_lines_range, entity_code_lines = sls_context_parser.extract_code_lines(sls_file_data)\n            if entity_lines_range:\n                skipped_checks = CfnContextParser.collect_skip_comments(entity_code_lines)\n                variable_evaluations = {}\n                entity = EntityDetails(sls_context_parser.provider_type, sls_file_data)\n                results = complete_registry.scan(sls_file, entity, skipped_checks, runner_filter)\n                tags = cfn_utils.get_resource_tags(entity, complete_registry)\n                for check, check_result in results.items():\n                    record = Record(check_id=check.id, check_name=check.name, check_result=check_result,\n                                    code_block=[],              # Don't show, could be large\n                                    file_path=sls_file,\n                                    file_line_range=entity_lines_range,\n                                    resource=\"complete\",        # Weird, not sure what to put where\n                                    evaluations=variable_evaluations,\n                                    check_class=check.__class__.__module__, file_abs_path=file_abs_path,\n                                    entity_tags=tags)\n                    record.set_guideline(check.guideline)\n                    report.add_record(record=record)\n\n        return report\n\n\ndef get_files_definitions(files: List[str], filepath_fn=None) \\\n        -> Tuple[Dict[str, DictNode], Dict[str, List[Tuple[int, str]]]]:\n    results = parallel_runner.run_function(lambda f: (f, parse(f)), files)\n    definitions = {}\n    definitions_raw = {}\n    for file, result in results:\n        if result:\n            path = filepath_fn(file) if filepath_fn else file\n            definitions[path], definitions_raw[path] = result\n\n    return definitions, definitions_raw\n", "path": "checkov/serverless/runner.py"}]}