def test_populate_na_requirement(self, get_cs_requirements, s3_stubber, special_account): """Test populate with requirement that doesn't apply to account""" cs_data = create_cloudsploit_data({}, [ { 'plugin': 'pluginNameOne', 'category': 'S3', 'title': 'Plugin Name One', 'resource': 'arn:aws:s3:::aaa-aaa-aaa-aaa', 'region': 'global', 'status': 'FAIL', 'statusNumber': 2, 'message': 'sample text' }, ]) get_cs_requirements.return_value = [{ 'requirementId': '10', 'weight': Decimal(100), 'description': 'sample text', 'source': 'cloudsploit', 'severity': 'medium', 'onlyAppliesTo': ['other-than-special'], 'cloudsploit': { 'finding': 'Plugin Name One' } }] s3_stubber.add_response('get_object', create_cloudsploit_s3_response(cs_data), { 'Bucket': os.getenv('CLOUDSPLOIT_RESULT_BUCKET'), 'Key': os.getenv('CLOUDSPLOIT_PREFIX') + '/' + special_account['accountId'] + '/latest.json', }) scan_id = scans_table.create_new_scan_id() cloudsploit_populate({'scanId': scan_id, 'accountId': special_account['accountId']}, {}) # check NCR record is created ncr_records_in_db = ncr_table.query_all(KeyConditionExpression=Key('scanId').eq(scan_id)) assert ncr_records_in_db == [] # check score record is created score_records_in_db = scores_table.query_all(KeyConditionExpression=Key('scanId').eq(scan_id)) for record in score_records_in_db: assert record.pop('ttl') assert score_records_in_db == [ scores_table.new_score( scan_id, special_account['accountId'], { 'requirementId': '10', 'severity': 'medium', 'weight': Decimal(100) }, scores_table.NOT_APPLICABLE, scores_table.NOT_APPLICABLE, ) ]
def account_detailed_scores_handler(event, context): """ :param event: :param context: :raises HttpInvalidException if missing 'scanId' or 'accountIds': :return account scores of all account_ids in event: """ try: account_ids = event['pathParameters']['accountIds'] scan_id = event['scanId'] except KeyError: raise HttpInvalidException( 'account ids or scan id not found in request') accounts = [] account_ids = urllib.parse.unquote(account_ids).split(',') require_can_read_account(event['userRecord'], account_ids) for account_id in account_ids: try: account_name = accounts_table.get_account(account_id).get( 'account_name', account_id) except KeyError: raise HttpNotFoundException( f'account record not found for {account_id}') if len(account_id) > 0: detailed_score = { 'accountId': account_id, 'accountName': account_name, } requirements = [] to_parse = scores_table.query_all( KeyConditionExpression=Key('scanId').eq(scan_id) & Key('accntId_rqrmntId').begins_with(account_id), ) for item in to_parse: requirements.append({ 'requirementId': item['requirementId'], 'score': item['score'] }) detailed_score['requirementsScores'] = requirements accounts.append(detailed_score) return {'accounts': accounts}
def load_scores(accounts: List) -> None: """ Queries accounts' scores from database and stores in CACHE Parameters: accounts (list): the account to get the scores for. """ scan_id = CACHED_SCAN['scanId'] if 'account_detail_scores' not in CACHE: CACHE['account_detail_scores'] = defaultdict(lambda: defaultdict(dict)) for account in accounts: account_id = account['accountId'] if account_id not in CACHE['account_detail_scores']: logger.debug('Querying account scores') account_scores = scores_table.query_all( KeyConditionExpression=Key('scanId').eq(scan_id) & Key('accntId_rqrmntId').begins_with(account_id) ) CACHE['account_detail_scores'][account_id] = defaultdict(dict, { score['requirementId']: score for score in account_scores })
def score_calc_handler(event, context): """ :param event: { scanId: string, accountIds: list of accountIds, } :param context: dict :return: None """ scan_id = event['openScan']['scanId'] account_ids = event['load']['accountIds'] date = scan_id[0:10] all_scores_to_put = [] all_account_scores = [] for account_id in account_ids: account_name = accounts_table.get_account(account_id).get('account_name') scores_to_put = { record['requirementId']: record for record in scores_table.query_all( KeyConditionExpression=Key('scanId').eq(scan_id) & Key('accntId_rqrmntId').begins_with(account_id) ) } existing_ncr_records = ncr_table.query_all( KeyConditionExpression=Key('scanId').eq(scan_id) & Key('accntId_rsrceId_rqrmntId').begins_with(account_id), ) grouped_ncr_data = defaultdict(list) for ncr_object in existing_ncr_records: grouped_ncr_data[ncr_object['requirementId']].append(ncr_object) for requirement_object in all_requirements: severity = requirement_object['severity'] record_to_edit = scores_to_put.get(requirement_object['requirementId'], False) if record_to_edit is False: continue # data not collected for this account for this scan for this requirement, moving on score_object = record_to_edit['score'][severity] # check if score is DNC if so we skip counting failing resources if scores_table.DATA_NOT_COLLECTED in score_object.values(): continue if score_object['numFailing'] is None: score_object['numFailing'] = Decimal(0) matching_ncrs = grouped_ncr_data.get(requirement_object['requirementId'], []) for ncr_record in matching_ncrs: is_excluded = ncr_record.get('exclusionApplied', False) # TODO handle hidden ncr's also (decrement numResources) if is_excluded: continue else: score_object['numFailing'] += 1 all_scores_to_put.append(record_to_edit) account_score = { 'accountId': account_id, 'accountName': account_name, 'date': date, 'scanId': scan_id, 'score': scores_table.weighted_score_aggregate_calc(scores_to_put.values()) } all_account_scores.append(account_score) scores_table.batch_put_records(all_scores_to_put) account_scores_table.batch_put_records(all_account_scores)
def test_populate(self, get_cs_requirements, s3_stubber, regular_account): cs_data = create_cloudsploit_data({}, [ { 'plugin': 'pluginNameOne', 'category': 'S3', 'title': 'Plugin Name One', 'resource': 'arn:aws:s3:::aaa-aaa-aaa-aaa', 'region': 'global', 'status': 'FAIL', 'statusNumber': 2, 'message': 'sample text' }, { 'plugin': 'pluginNameOne', 'category': 'S3', 'title': 'Plugin Name Two', 'resource': 'arn:aws:s3:::aaa-aaa-aaa-aaa', 'region': 'global', 'status': 'FAIL', 'statusNumber': 2, 'message': 'sample text' }, { 'plugin': 'pluginNameOne', 'category': 'S3', 'title': 'Plugin Name Two', 'resource': 'arn:aws:s3:::aaa-aaa-aaa-aaa', 'region': 'global', 'status': 'FAIL', 'statusNumber': 2, 'message': 'sample text2' }, ]) get_cs_requirements.return_value = [{ 'requirementId': '10', 'weight': Decimal(100), 'description': 'sample text', 'source': 'cloudsploit', 'severity': 'medium', 'service': 'organizations', 'component': 'account', 'cloudsploit': { 'finding': [ 'Plugin Name One', 'Plugin Name Two', ] } }] s3_stubber.add_response('get_object', create_cloudsploit_s3_response(cs_data), { 'Bucket': os.getenv('CLOUDSPLOIT_RESULT_BUCKET'), 'Key': os.getenv('CLOUDSPLOIT_PREFIX') + '/' + regular_account['accountId'] + '/latest.json', }) scan_id = scans_table.create_new_scan_id() cloudsploit_populate({'scanId': scan_id, 'accountId': regular_account['accountId']}, {}) # check NCR record is created ncr_records_in_db = ncr_table.query_all(KeyConditionExpression=Key('scanId').eq(scan_id)) for record in ncr_records_in_db: assert record.pop('ttl') assert ncr_records_in_db == [ ncr_table.new_ncr_record({ 'accountId': regular_account['accountId'], 'accountName': regular_account['account_name'], 'cloudsploitStatus': 'FAIL', 'requirementId': '10', 'resourceId': 'aaa-aaa-aaa-aaa', 'resourceType': 'organizations-account', 'region': 'global', 'reason': 'sample text\nsample text2', }, scan_id) ] # check score record is created score_records_in_db = scores_table.query_all(KeyConditionExpression=Key('scanId').eq(scan_id)) for record in score_records_in_db: assert record.pop('ttl') assert score_records_in_db == [ scores_table.new_score( scan_id, regular_account['accountId'], { 'requirementId': '10', 'severity': 'medium', 'weight': Decimal(100) }, Decimal(3) ) ]