def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, IAMManagedPolicy) and resource.Properties.Users: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource }, ) return result
def test_with_templates(cf_path): with open(cf_path) as cf_script: cf_template = convert_json_or_yaml_to_dict(cf_script.read()) config = Config(project_name=cf_path, service_name=cf_path, stack_name=cf_path, rules=DEFAULT_RULES.keys()) # Scan result result = Result() cfmodel = pycfmodel.parse(cf_template).resolve() rules = [DEFAULT_RULES.get(rule)(config, result) for rule in config.rules] processor = RuleProcessor(*rules) processor.process_cf_template(cfmodel, config, result) # Use this to print the stack if there'IAMManagedPolicyWildcardActionRule an error if len(result.exceptions): print(cf_path) traceback.print_tb(result.exceptions[0].__traceback__) assert len(result.exceptions) == 0
def test_security_group_rules_as_refs(self): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "SecurityGroupIngress": [{"CidrIp": {"Ref": "MyParam"}, "FromPort": 22, "ToPort": 22}] }, } }, } result = Result() rule = SecurityGroupOpenToWorldRule(None, result) resources = parse(role_props).resources rule.invoke(resources, []) assert result.valid assert len(result.failed_rules) == 0
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, (IAMManagedPolicy, IAMPolicy, S3BucketPolicy, SNSTopicPolicy, SQSQueuePolicy)): self.check_for_wildcards(result, logical_id, resource.Properties.PolicyDocument, extras) elif isinstance(resource, (IAMRole, IAMUser)): if isinstance(resource, IAMRole): self.check_for_wildcards( result, logical_id, resource.Properties.AssumeRolePolicyDocument, extras) if resource.Properties and resource.Properties.Policies: for policy in resource.Properties.Policies: self.check_for_wildcards(result, logical_id, policy.PolicyDocument, extras) return result
def resource_invoke(self, resource: S3Bucket, logical_id: str, extras: Optional[Dict] = None) -> Result: result = Result() version_configuration_status = getattr( resource.Properties.VersioningConfiguration, "Status", None) if version_configuration_status != self.ENABLED_STATUS: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource }, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.resources_filtered_by_type( "AWS::EC2::Volume").items(): encrypted_status = getattr(resource.Properties, "Encrypted", None) if encrypted_status is None or encrypted_status is False: self.add_failure_to_result( result, self.REASON.format(logical_id), resource_ids={logical_id}, resource_types={resource.Type}, context={ "config": self._config, "extras": extras, "logical_id": logical_id, "resource": resource }, ) return result
def invoke(self, cfmodel: CFModel, extras: Optional[Dict] = None) -> Result: result = Result() for logical_id, resource in cfmodel.Resources.items(): if isinstance(resource, IAMRole): # check AssumeRolePolicyDocument. if resource.Properties.AssumeRolePolicyDocument.allowed_actions_with( REGEX_WILDCARD_POLICY_ACTION): self.add_failure_to_result( result, self.REASON.format(logical_id, "AssumeRolePolicy"), resource_ids={logical_id}, ) # check other policies of the IAM role. if resource.Properties.Policies: for policy in resource.Properties.Policies: if policy.PolicyDocument.allowed_actions_with( REGEX_WILDCARD_POLICY_ACTION): self.add_failure_to_result( result, self.REASON.format( logical_id, f"{policy.PolicyName} policy"), resource_ids={logical_id}, ) # check AWS::IAM::ManagedPolicy. elif isinstance( resource, IAMManagedPolicy ) and resource.Properties.PolicyDocument.allowed_actions_with( REGEX_WILDCARD_POLICY_ACTION): self.add_failure_to_result( result, self.REASON.format(logical_id, "AWS::IAM::ManagedPolicy"), resource_ids={logical_id}, ) return result
def resource_invoke(self, resource: S3BucketPolicy, logical_id: str) -> Result: result = Result() for statement in resource.Properties.PolicyDocument._statement_as_list( ): for principal in statement.get_principal_list(): account_id = get_account_id_from_principal(principal) if not account_id: continue if account_id not in self.valid_principals: if statement.Condition and statement.Condition.dict(): logger.warning( f"Not adding {type(self).__name__} failure in {logical_id} " f"because there are conditions: {statement.Condition}" ) else: self.add_failure_to_result( result, self.REASON.format(logical_id, account_id), resource_ids={logical_id}, ) return result
def test_invalid_security_group_multiple_statements(self): role_props = { "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "RootRole": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "SecurityGroupIngress": [ {"CidrIp": "10.0.0.0/8", "FromPort": 22, "ToPort": 22}, {"CidrIp": "0.0.0.0/0", "FromPort": 9090, "ToPort": 9090}, ] }, } }, } result = Result() rule = SecurityGroupOpenToWorldRule(None, result) resources = parse(role_props).resources rule.invoke(resources, []) assert result.failed_rules[0]["reason"] == 'Port 9090 open to the world in security group "RootRole"' assert result.failed_rules[0]["rule"] == "SecurityGroupOpenToWorldRule"
def test_with_templates(self): dir_path = os.path.dirname(os.path.realpath(__file__)) test_templates = glob.glob('{}/test_templates/*.*'.format(dir_path)) for template in test_templates: cf_script = open(template) cf_template = S3Adapter().convert_json_or_yaml_to_dict( cf_script.read()) config = Config( project_name=template, service_name=template, stack_name=template, rules=ALL_RULES.keys(), ) # Scan result result = Result() rules = [ ALL_RULES.get(rule)(config, result) for rule in config.RULES ] processor = RuleProcessor(*rules) processor.process_cf_template(cf_template, config, result) # Use this to print the stack if there's an error if len(result.exceptions): print(template) traceback.print_tb(result.exceptions[0].__traceback__) no_resource_templates = [ 'vulgar_bad_syntax.yml', 'rubbish.json', ] if template.split('/')[-1] in no_resource_templates: assert len(result.exceptions) == 1 else: assert len(result.exceptions) == 0
def test_failures_are_raised(bad_template): result = Result() rule = GenericWildcardPrincipalRule(None, result) rule.invoke(bad_template) assert result.valid assert len(result.failed_rules) == 0 assert len(result.failed_monitored_rules) == 3 assert result.failed_monitored_rules[ 0].rule == "GenericWildcardPrincipalRule" assert ( result.failed_monitored_rules[0].reason == "PolicyA should not allow wildcard in principals or account-wide principals " "(principal: 'somewhatrestricted:*')") assert result.failed_monitored_rules[ 1].rule == "GenericWildcardPrincipalRule" assert result.failed_monitored_rules[ 1].reason == "PolicyA contains an unknown principal: 123445" assert result.failed_monitored_rules[ 2].rule == "GenericWildcardPrincipalRule" assert ( result.failed_monitored_rules[2].reason == "PolicyA should not allow wildcard in principals or account-wide principals " "(principal: 'arn:aws:iam::123445:*')")