Repository: aws-cloudformation/cfn-lint

Issue:
E3031 CidrIp contains invalid characters fails when Fn::Sub is present
*cfn-lint version: (`cfn-lint --version`)*

`cfn-lint 0.16.0`

*Description of issue.*

When `CidrIp` value is `!Sub`ed from `Parameters` - E3031 lint error is raised. Sample template:

```lang=yaml
AWSTemplateFormatVersion: 2010-09-09

Description: AMI Builder Stack

Parameters:

  BuilderCidr:
    Type: String

Resources:

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Description
      VpcId: vpc-id
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 1
        ToPort: 65535
        CidrIp: !Sub ${BuilderCidr}
```

Expected output: successful lint
Actual output:

```
E3031 CidrIp contains invalid characters (Pattern: x.x.x.x/y) at Resources/SecurityGroup/Properties/SecurityGroupIngress/0/CidrIp/Fn::Sub
```

> Cfn-lint uses the [CloudFormation Resource Specifications](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html) as the base to do validation. These files are included as part of the application version. Please update to the latest version of `cfn-lint` or update the spec files manually (`cfn-lint -u`)

The problem still persists after running `cfn-lint -u`


Discussion:
Variables are replaced with ""... In this case a `!Ref` would be more "logical" btw, but the rule should not break ofcourse

Resolving patch:
diff --git a/src/cfnlint/rules/resources/properties/AllowedPattern.py b/src/cfnlint/rules/resources/properties/AllowedPattern.py
--- a/src/cfnlint/rules/resources/properties/AllowedPattern.py
+++ b/src/cfnlint/rules/resources/properties/AllowedPattern.py
@@ -15,7 +15,6 @@
   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 """
 import re
-import six
 from cfnlint import CloudFormationLintRule
 from cfnlint import RuleMatch
 
@@ -37,21 +36,6 @@ def initialize(self, cfn):
         for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):
             self.resource_sub_property_types.append(property_type_spec)
 
-    def check_sub(self, value, path, property_name, **kwargs):
-        """Check Value of a Sub"""
-        matches = []
-
-        if isinstance(value, list):
-            if isinstance(value[0], six.string_types):
-                # Remove the sub (${}) from the value
-                stripped_value = re.sub(r'\${.*}', '', value[0])
-                matches.extend(self.check_value(stripped_value, path[:] + [0], property_name, **kwargs))
-        else:
-            # Remove the sub (${}) from the value
-            stripped_value = re.sub(r'\${.*}', '', value)
-            matches.extend(self.check_value(stripped_value, path[:], property_name, **kwargs))
-        return matches
-
     def check_value(self, value, path, property_name, **kwargs):
         """Check Value"""
         matches = []
@@ -86,7 +70,6 @@ def check(self, cfn, properties, value_specs, property_specs, path):
                             cfn.check_value(
                                 p_value, prop, p_path,
                                 check_value=self.check_value,
-                                check_sub=self.check_sub,
                                 value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get('ValueTypes').get(value_type, {}),
                                 cfn=cfn, property_type=property_type, property_name=prop
                             )


Test patch:
diff --git a/test/rules/resources/properties/test_allowed_pattern.py b/test/rules/resources/properties/test_allowed_pattern.py
--- a/test/rules/resources/properties/test_allowed_pattern.py
+++ b/test/rules/resources/properties/test_allowed_pattern.py
@@ -44,7 +44,7 @@ def test_file_positive(self):
 
     def test_file_negative_sg_description(self):
         """Test failure"""
-        self.helper_file_negative('test/fixtures/templates/bad/properties_sg_description.yaml', 3)
+        self.helper_file_negative('test/fixtures/templates/bad/properties_sg_description.yaml', 1)
 
     def test_file_negative_sg_ingress(self):
         """Test failure"""