Repository: aws-cloudformation/cfn-lint

Issue:
Rule E3028 throws exception if RDS::DBCluster is present without EngineMode specified
*cfn-lint version: `0.28.0`*

*Description of issue.*
Error thrown:
```
E0002 Unknown exception while processing rule E3028: 'NoneType' object has no attribute 'items_safe'
```
Debug log:
```
2020-02-17 15:24:57,775 - cfnlint - DEBUG - Completed linting of file: .\test.yml
E0002 Unknown exception while processing rule E3028: Traceback (most recent call last):
  File "c:\python36\lib\site-packages\cfnlint\rules\__init__.py", line 203, in run_check
    return check(*args)
  File "c:\python36\lib\site-packages\cfnlint\rules\__init__.py", line 89, in wrapper
    results = match_function(self, filename, cfn, *args, **kwargs)
  File "c:\python36\lib\site-packages\cfnlint\rules\__init__.py", line 122, in matchall_resource_properties
    return self.match_resource_properties(resource_properties, property_type, path, cfn)  # pylint: disable=E1102
  File "c:\python36\lib\site-packages\cfnlint\rules\resources\rds\AuroraScalingConfiguration.py", line 44, in match_resource_properties
    matches.extend(self.check(properties, path, cfn))
  File "c:\python36\lib\site-packages\cfnlint\rules\resources\rds\AuroraScalingConfiguration.py", line 30, in check
    engine = properties.get_safe('EngineMode', type_t=six.string_types)
  File "c:\python36\lib\site-packages\cfnlint\decode\node.py", line 125, in get_safe
    for sub_v, sub_path in value.items_safe(path + [key]):
AttributeError: 'NoneType' object has no attribute 'items_safe'
.\test.yml:1:1
```

Breaking template:
```
---
AWSTemplateFormatVersion: 2010-09-09

Resources:
  DatabaseCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      Engine: aurora-postgresql
      #EngineMode: provisioned
      EngineVersion: "10.5"
```
Working template:
```
---
AWSTemplateFormatVersion: 2010-09-09

Resources:
  DatabaseCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      Engine: aurora-postgresql
      EngineMode: provisioned
      EngineVersion: "10.5"
```

Problem is around here: https://github.com/aws-cloudformation/cfn-python-lint/blob/084f832f50408c8bea3f087f48d9b38e4e5a5c9d/src/cfnlint/rules/resources/rds/AuroraScalingConfiguration.py#L26-L31

I'm suspecting that `get_value_from_scenario` is returning `None` and then the rule isn't checking for that: https://github.com/aws-cloudformation/cfn-python-lint/blob/0757dc4c7ba108f47c19eb0917b0e6e37ee62d83/src/cfnlint/__init__.py#L825


Discussion:

Resolving patch:
diff --git a/src/cfnlint/decode/node.py b/src/cfnlint/decode/node.py
--- a/src/cfnlint/decode/node.py
+++ b/src/cfnlint/decode/node.py
@@ -117,14 +117,23 @@ def get_safe(self, key, default=None, path=None, type_t=()):
             """
             path = path or []
             value = self.get(key, default)
+            if value is None and default is None:
+                # if default is None and value is None return empty list
+                return []
+
+            # if the value is the default make sure that the default value is of type_t when specified
+            if bool(type_t) and value == default and not isinstance(default, type_t):
+                raise ValueError('"default" type should be of "type_t"')
+
+            # when not a dict see if if the value is of the right type
+            results = []
             if not isinstance(value, (dict)):
                 if isinstance(value, type_t) or not type_t:
                     return [(value, (path[:] + [key]))]
-
-            results = []
-            for sub_v, sub_path in value.items_safe(path + [key]):
-                if isinstance(sub_v, type_t) or not type_t:
-                    results.append((sub_v, sub_path))
+            else:
+                for sub_v, sub_path in value.items_safe(path + [key]):
+                    if isinstance(sub_v, type_t) or not type_t:
+                        results.append((sub_v, sub_path))
 
             return results
 


Test patch: