""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ import regex as re from cfnlint.rules import CloudFormationLintRule, RuleMatch class HardCodedArnProperties(CloudFormationLintRule): """Checks Resources if ARNs use correctly placed Pseudo Parameters instead of hardcoded Partition, Region, and Account Number""" id = "I3042" shortdesc = "ARNs should use correctly placed Pseudo Parameters" description = "Checks Resources if ARNs use correctly placed Pseudo Parameters instead of hardcoded Partition, Region, and Account Number" source_url = "" tags = ["resources"] regex = re.compile( r"arn:(\$\{[^:]*::[^:]*}|[^:]*):[^:]+:(\$\{[^:]*::[^:]*}|[^:]*):(\$\{[^:]*::[^:]*}|[^:]*)" ) def __init__(self): """Init""" super().__init__() self.config_definition = { "partition": { "default": True, "type": "boolean", }, "region": { "default": False, "type": "boolean", }, "accountId": { "default": False, "type": "boolean", }, } self.configure() def _match_values(self, cfnelem, path): """Recursively search for values matching the searchRegex""" values = [] if isinstance(cfnelem, dict): for key in cfnelem: pathprop = path[:] pathprop.append(key) values.extend(self._match_values(cfnelem[key], pathprop)) elif isinstance(cfnelem, list): for index, item in enumerate(cfnelem): pathprop = path[:] pathprop.append(index) values.extend(self._match_values(item, pathprop)) else: # Leaf node if isinstance(cfnelem, str): # and re.match(searchRegex, cfnelem): for variable in re.findall(self.regex, cfnelem): if "Fn::Sub" in path: values.append(path + [variable]) return values def match_values(self, cfn): """ Search for values in all parts of the templates that match the searchRegex """ results = [] results.extend(self._match_values(cfn.template.get("Resources", {}), [])) # Globals are removed during a transform. They need to be checked manually results.extend(self._match_values(cfn.template.get("Globals", {}), [])) return results def match(self, cfn): matches = [] transforms = cfn.transform_pre["Transform"] transforms = transforms if isinstance(transforms, list) else [transforms] if "AWS::Serverless-2016-10-31" in cfn.transform_pre["Transform"]: return matches # Get a list of paths to every leaf node string containing at least one ${parameter} parameter_string_paths = self.match_values(cfn) # We want to search all of the paths to check if each one contains an 'Fn::Sub' for parameter_string_path in parameter_string_paths: path = ["Resources"] + parameter_string_path[:-1] candidate = parameter_string_path[-1] # !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # is valid even with aws as the account #. This handles empty string if self.config["partition"] and not re.match( r"^\$\{\w+}|\$\{AWS::Partition}|$", candidate[0] ): # or not re.match(r'^(\$\{\w+}|\$\{AWS::Region}|)$', candidate[1]) or not re.match(r'^\$\{\w+}|\$\{AWS::AccountId}|aws|$', candidate[2]): message = "ARN in Resource {0} contains hardcoded Partition in ARN or incorrectly placed Pseudo Parameters" matches.append(RuleMatch(path, message.format(path[1]))) if self.config["region"] and not re.match( r"^(\$\{\w+}|\$\{AWS::Region}|)$", candidate[1] ): # or or not re.match(r'^\$\{\w+}|\$\{AWS::AccountId}|aws|$', candidate[2]): message = "ARN in Resource {0} contains hardcoded Region in ARN or incorrectly placed Pseudo Parameters" matches.append(RuleMatch(path, message.format(path[1]))) if self.config["accountId"] and not re.match( r"^\$\{\w+}|\$\{AWS::AccountId}|aws|$", candidate[2] ): message = "ARN in Resource {0} contains hardcoded AccountId in ARN or incorrectly placed Pseudo Parameters" matches.append(RuleMatch(path, message.format(path[1]))) return matches
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
backup | Folder | 0755 |
|
|
certificatemanager | Folder | 0755 |
|
|
cloudformation | Folder | 0755 |
|
|
cloudfront | Folder | 0755 |
|
|
codepipeline | Folder | 0755 |
|
|
dynamodb | Folder | 0755 |
|
|
ecs | Folder | 0755 |
|
|
ectwo | Folder | 0755 |
|
|
elasticache | Folder | 0755 |
|
|
elb | Folder | 0755 |
|
|
events | Folder | 0755 |
|
|
iam | Folder | 0755 |
|
|
lmbd | Folder | 0755 |
|
|
properties | Folder | 0755 |
|
|
rds | Folder | 0755 |
|
|
route53 | Folder | 0755 |
|
|
stepfunctions | Folder | 0755 |
|
|
updatepolicy | Folder | 0755 |
|
|
ApproachingLimitName.py | File | 686 B | 0644 |
|
ApproachingLimitNumber.py | File | 690 B | 0644 |
|
BothUpdateReplacePolicyDeletionPolicyNeeded.py | File | 1.74 KB | 0644 |
|
CircularDependency.py | File | 1.18 KB | 0644 |
|
Configuration.py | File | 6.97 KB | 0644 |
|
DeletionPolicy.py | File | 3.91 KB | 0644 |
|
DependsOn.py | File | 2.7 KB | 0644 |
|
DependsOnObsolete.py | File | 3.01 KB | 0644 |
|
HardCodedArnProperties.py | File | 4.71 KB | 0644 |
|
LimitName.py | File | 704 B | 0644 |
|
LimitNumber.py | File | 692 B | 0644 |
|
Modules.py | File | 2.8 KB | 0644 |
|
Name.py | File | 688 B | 0644 |
|
NoEcho.py | File | 3.2 KB | 0644 |
|
PreviousGenerationInstanceType.py | File | 4.2 KB | 0644 |
|
ResourceSchema.py | File | 1.74 KB | 0644 |
|
RetentionPeriodOnResourceTypesWithAutoExpiringContent.py | File | 6.42 KB | 0644 |
|
ServerlessTransform.py | File | 1.59 KB | 0644 |
|
UniqueNames.py | File | 924 B | 0644 |
|
UpdateReplacePolicy.py | File | 4.07 KB | 0644 |
|
UpdateReplacePolicyDeletionPolicyOnStatefulResourceTypes.py | File | 2.09 KB | 0644 |
|
__init__.py | File | 106 B | 0644 |
|