""" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 """ import regex as re from cfnlint.helpers import REGEX_DYN_REF, REGEX_DYN_REF_SSM from cfnlint.rules import CloudFormationLintRule, RuleMatch class Password(CloudFormationLintRule): """Check if Password Properties are properly configured""" id = "W2501" shortdesc = "Check if Password Properties are correctly configured" description = ( "Password properties should not be strings and if parameter using NoEcho" ) source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html#creds" tags = ["parameters", "passwords", "security", "dynamic reference"] def match(self, cfn): """Check CloudFormation Password Parameters""" matches = [] password_properties = [ "AccountPassword", "AdminPassword", "ADDomainJoinPassword", "CrossRealmTrustPrincipalPassword", "KdcAdminPassword", "Password", "DbPassword", "MasterUserPassword", "PasswordParam", ] parameters = cfn.get_parameter_names() fix_params = [] for password_property in password_properties: # Build the list of refs refs = cfn.search_deep_keys(password_property) trees = [] for tree in refs: if len(tree) > 2: if tree[0] == "Resources" and tree[2] == "Properties": trees.append(tree) for tree in trees: obj = tree[-1] if isinstance(obj, (str)): if re.match(REGEX_DYN_REF, obj): if re.match(REGEX_DYN_REF_SSM, obj): message = f'Password should use a secure dynamic reference for {"/".join(map(str, tree[:-1]))}' matches.append(RuleMatch(tree[:-1], message)) else: message = f'Password shouldn\'t be hardcoded for {"/".join(map(str, tree[:-1]))}' matches.append(RuleMatch(tree[:-1], message)) elif isinstance(obj, dict): if len(obj) == 1: for key, value in obj.items(): if key == "Ref": if value in parameters: param = cfn.template["Parameters"][value] if "NoEcho" in param: if not param["NoEcho"]: fix_params.append( { "Name": value, "Use": password_property, } ) else: fix_params.append( {"Name": value, "Use": password_property} ) else: message = f'Inappropriate map found for password on {"/".join(map(str, tree[:-1]))}' matches.append(RuleMatch(tree[:-1], message)) for paramname in fix_params: message = f'Parameter {paramname["Name"]} used as {paramname["Use"]}, therefore NoEcho should be True' tree = ["Parameters", paramname["Name"]] matches.append(RuleMatch(tree, message)) return matches
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
AllowedPattern.py | File | 5.76 KB | 0644 |
|
AllowedValue.py | File | 4.8 KB | 0644 |
|
AtLeastOne.py | File | 4.11 KB | 0644 |
|
AvailabilityZone.py | File | 3.41 KB | 0644 |
|
BasedOnValue.py | File | 6.33 KB | 0644 |
|
Exclusive.py | File | 3.98 KB | 0644 |
|
ImageId.py | File | 2.37 KB | 0644 |
|
Inclusive.py | File | 3.7 KB | 0644 |
|
JsonSize.py | File | 6.08 KB | 0644 |
|
ListDuplicates.py | File | 4.39 KB | 0644 |
|
ListDuplicatesAllowed.py | File | 4.76 KB | 0644 |
|
ListSize.py | File | 4.88 KB | 0644 |
|
NumberSize.py | File | 4.88 KB | 0644 |
|
OnlyOne.py | File | 3.89 KB | 0644 |
|
Password.py | File | 3.63 KB | 0644 |
|
Properties.py | File | 27.49 KB | 0644 |
|
PropertiesTemplated.py | File | 2.44 KB | 0644 |
|
Required.py | File | 4.1 KB | 0644 |
|
RequiredBasedOnValue.py | File | 831 B | 0644 |
|
StringSize.py | File | 4.52 KB | 0644 |
|
UnwantedBasedOnValue.py | File | 837 B | 0644 |
|
ValuePrimitiveType.py | File | 11.6 KB | 0644 |
|
ValueRefGetAtt.py | File | 11.96 KB | 0644 |
|
__init__.py | File | 106 B | 0644 |
|