def test_extract_arns_from_statement_condition(self): test_condition_list = [ 'ArnEquals', 'ForAllValues:ArnEquals', 'ForAnyValue:ArnEquals', 'ArnLike', 'ForAllValues:ArnLike', 'ForAnyValue:ArnLike', 'StringLike', 'ForAllValues:StringLike', 'ForAnyValue:StringLike', 'StringEquals', 'ForAllValues:StringEquals', 'ForAnyValue:StringEquals' ] bad_condition_list = [ 'NotACondition', 'ArnLikeSomethingNotARealCondition' ] arn_types = [ ('aws:sourcearn', 'arn:aws:s3:::some-s3-bucket'), ('aws:sourcearn', 'arn:aws:s3:::some-s3-bucket/*'), ('aws:sourcearn', "*"), ('aws:sourceowner', '012345678912'), ('aws:sourceowner', '*') ] for condition in test_condition_list: for arn_type in arn_types: test_condition = { condition: { arn_type[0]: arn_type[1] } } result = ARN.extract_arns_from_statement_condition(test_condition) self.assertIsInstance(result, list) self.assertTrue(len(result) > 0) for condition in bad_condition_list: for arn_type in arn_types: test_condition = { condition: { arn_type[0]: arn_type[1] } } result = ARN.extract_arns_from_statement_condition(test_condition) self.assertIsInstance(result, list) self.assertTrue(len(result) == 0)
def check_sqsqueue_crossaccount(self, sqsitem): """ alert on cross account access """ policy = sqsitem.config for statement in policy.get("Statement", []): account_numbers = [] princ = statement.get("Principal", None) if not princ: # It is possible not to define a principal, AWS ignores these statements. # We should raise an issue. tag = "SQS Policy is lacking Principal field" notes = json.dumps(statement) self.add_issue(5, tag, sqsitem, notes=notes) continue if isinstance(princ, dict): princ_val = princ.get("AWS") or princ.get("Service") else: princ_val = princ if princ_val == "*": condition = statement.get('Condition', {}) arns = ARN.extract_arns_from_statement_condition(condition) if not arns: tag = "SQS Queue open to everyone" notes = "An SQS policy where { 'Principal': { 'AWS': '*' } } must also have" notes += " a {'Condition': {'ArnEquals': { 'AWS:SourceArn': '<ARN>' } } }" notes += " or it is open to the world. In this case, anyone is allowed to perform " notes += " this action(s): {}".format( statement.get("Action")) self.add_issue(10, tag, sqsitem, notes=notes) for arn in arns: self._parse_arn(arn, account_numbers, sqsitem) else: if isinstance(princ_val, list): for entry in princ_val: arn = ARN(entry) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', sqsitem, notes=entry) continue if not arn.service: account_numbers.append(arn.account_number) else: arn = ARN(princ_val) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', sqsitem, notes=princ_val) elif not arn.service: account_numbers.append(arn.account_number) for account_number in account_numbers: self._check_cross_account(account_number, sqsitem, 'policy')
def check_snstopicpolicy_crossaccount(self, snsitem): """ alert on cross account access """ policy = snsitem.config.get('policy', {}) for statement in policy.get("Statement", []): account_numbers = [] princ = statement.get("Principal", {}) if isinstance(princ, dict): princ_val = princ.get("AWS") or princ.get("Service") else: princ_val = princ if princ_val == "*": condition = statement.get('Condition', {}) arns = ARN.extract_arns_from_statement_condition(condition) if not arns: tag = "SNS Topic open to everyone" notes = "An SNS policy where { 'Principal': { 'AWS': '*' } } must also have" notes += " a {'Condition': {'StringEquals': { 'AWS:SourceOwner': '<ARN>' } } }" notes += " or it is open to the world. In this case, anyone is allowed to perform " notes += " this action(s): {}".format( statement.get("Action")) self.add_issue(10, tag, snsitem, notes=notes) for arn in arns: self._parse_arn(arn, account_numbers, snsitem) else: if isinstance(princ_val, list): for entry in princ_val: arn = ARN(entry) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', snsitem, notes=entry) continue if not arn.service: account_numbers.append(arn.account_number) else: arn = ARN(princ_val) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', snsitem, notes=princ_val) elif not arn.service: account_numbers.append(arn.account_number) for account_number in account_numbers: self._check_cross_account(account_number, snsitem, 'policy')
def check_sqsqueue_crossaccount(self, sqsitem): """ alert on cross account access """ policy = sqsitem.config for statement in policy.get("Statement", []): account_numbers = [] princ = statement.get("Principal", None) if not princ: # It is possible not to define a principal, AWS ignores these statements. # We should raise an issue. tag = "SQS Policy is lacking Principal field" notes = json.dumps(statement) self.add_issue(5, tag, sqsitem, notes=notes) continue if isinstance(princ, dict): princ_val = princ.get("AWS") or princ.get("Service") else: princ_val = princ if princ_val == "*": condition = statement.get('Condition', {}) arns = ARN.extract_arns_from_statement_condition(condition) if not arns: tag = "SQS Queue open to everyone" notes = "An SQS policy where { 'Principal': { 'AWS': '*' } } must also have" notes += " a {'Condition': {'ArnEquals': { 'AWS:SourceArn': '<ARN>' } } }" notes += " or it is open to the world. In this case, anyone is allowed to perform " notes += " this action(s): {}".format(statement.get("Action")) self.add_issue(10, tag, sqsitem, notes=notes) for arn in arns: self._parse_arn(arn, account_numbers, sqsitem) else: if isinstance(princ_val, list): for entry in princ_val: arn = ARN(entry) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', sqsitem, notes=entry) continue if not arn.service: account_numbers.append(arn.account_number) else: arn = ARN(princ_val) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', sqsitem, notes=princ_val) elif not arn.service: account_numbers.append(arn.account_number) for account_number in account_numbers: self._check_cross_account(account_number, sqsitem, 'policy')
def test_extract_arns_from_statement_condition(self): test_condition_list = [ 'ArnEquals', 'ForAllValues:ArnEquals', 'ForAnyValue:ArnEquals', 'ArnLike', 'ForAllValues:ArnLike', 'ForAnyValue:ArnLike', 'StringLike', 'ForAllValues:StringLike', 'ForAnyValue:StringLike', 'StringEquals', 'ForAllValues:StringEquals', 'ForAnyValue:StringEquals' ] bad_condition_list = [ 'NotACondition', 'ArnLikeSomethingNotARealCondition' ] arn_types = [('aws:sourcearn', 'arn:aws:s3:::some-s3-bucket'), ('aws:sourcearn', 'arn:aws:s3:::some-s3-bucket/*'), ('aws:sourcearn', "*"), ('aws:sourceowner', '012345678912'), ('aws:sourceowner', '*')] for condition in test_condition_list: for arn_type in arn_types: test_condition = {condition: {arn_type[0]: arn_type[1]}} result = ARN.extract_arns_from_statement_condition( test_condition) self.assertIsInstance(result, list) self.assertTrue(len(result) > 0) for condition in bad_condition_list: for arn_type in arn_types: test_condition = {condition: {arn_type[0]: arn_type[1]}} result = ARN.extract_arns_from_statement_condition( test_condition) self.assertIsInstance(result, list) self.assertTrue(len(result) == 0)
def check_snstopicpolicy_crossaccount(self, snsitem): """ alert on cross account access """ policy = snsitem.config.get('policy', {}) for statement in policy.get("Statement", []): account_numbers = [] princ = statement.get("Principal", {}) if isinstance(princ, dict): princ_val = princ.get("AWS") or princ.get("Service") else: princ_val = princ if princ_val == "*": condition = statement.get('Condition', {}) arns = ARN.extract_arns_from_statement_condition(condition) if not arns: tag = "SNS Topic open to everyone" notes = "An SNS policy where { 'Principal': { 'AWS': '*' } } must also have" notes += " a {'Condition': {'StringEquals': { 'AWS:SourceOwner': '<ARN>' } } }" notes += " or it is open to the world. In this case, anyone is allowed to perform " notes += " this action(s): {}".format(statement.get("Action")) self.add_issue(10, tag, snsitem, notes=notes) for arn in arns: self._parse_arn(arn, account_numbers, snsitem) else: if isinstance(princ_val, list): for entry in princ_val: arn = ARN(entry) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', snsitem, notes=entry) continue if not arn.service: account_numbers.append(arn.account_number) else: arn = ARN(princ_val) if arn.error: self.add_issue(3, 'Auditor could not parse ARN', snsitem, notes=princ_val) elif not arn.service: account_numbers.append(arn.account_number) for account_number in account_numbers: self._check_cross_account(account_number, snsitem, 'policy')