def test_send_analysis_by_file_with_zip_password_adds_zip_extension(self): # Arrange with responses.RequestsMock() as mock: mock.add('POST', url=self.full_url + '/analyze', status=201, json={'result_url': 'a/sd/asd'}) mock.add('GET', url=self.full_url + '/analyses/asd', status=200, json={ 'result': 'report', 'status': 'succeeded' }) analysis = FileAnalysis(file_path='a', zip_password='******') with patch(self.patch_prop, mock_open(read_data='data')): # Act analysis.send(wait=True) # Assert self.assertEqual(analysis.status, consts.AnalysisStatusCode.FINISH) self.assertEqual(analysis.result(), 'report') request_body = mock.calls[0].request.body.decode() self.assertTrue( 'Content-Disposition: form-data; name="zip_password"\r\n\r\nasd' in request_body) self.assertTrue( 'Content-Disposition: form-data; name="file"; filename="a.zip"' in request_body)
def test_send_analysis_by_file_with_disable_unpacking(self): # Arrange with responses.RequestsMock() as mock: mock.add('POST', url=self.full_url + '/analyze', status=201, json={'result_url': 'a/sd/asd'}) mock.add('GET', url=self.full_url + '/analyses/asd', status=200, json={ 'result': 'report', 'status': 'succeeded' }) analysis = FileAnalysis(file_path='a', disable_dynamic_unpacking=True, disable_static_unpacking=True) with patch(self.patch_prop, mock_open(read_data='data')): # Act analysis.send(wait=True) # Assert self.assertEqual(analysis.status, consts.AnalysisStatusCode.FINISH) self.assertEqual(analysis.result(), 'report') request_body = mock.calls[0].request.body.decode() self.assertTrue( 'Content-Disposition: form-data; name="disable_static_extraction"\r\n\r\nTrue' in request_body) self.assertTrue( 'Content-Disposition: form-data; name="disable_dynamic_execution"\r\n\r\nTrue' in request_body)
def get_analysis_family(analysis: FileAnalysis, software_type_priorities: List[str], should_use_largest_families: bool = True) -> Tuple[Optional[str], Optional[int]]: result = analysis.result() family_name = result.get('family_name') if family_name: reused_gene_count = get_analysis_family_by_family_id(analysis, result['family_id']) return family_name, reused_gene_count if should_use_largest_families: largest_family_by_software_type = find_largest_family(analysis) for software_type in software_type_priorities: if software_type in largest_family_by_software_type: family = largest_family_by_software_type[software_type] return family['family_name'], family['reused_gene_count'] return None, None
def get_analysis_summary_metadata(analysis: FileAnalysis, use_hash_link: bool = False, should_use_largest_families: bool = True) -> Dict[str, any]: result = analysis.result() verdict = result['verdict'].lower() sub_verdict = result['sub_verdict'].lower() analysis_url = f"{ANALYZE_URL}/files/{result['sha256']}?private=true" if use_hash_link else result['analysis_url'] main_family = None gene_count = None iocs = None dynamic_ttps = None related_samples_unique_count = None software_type_priorities_by_verdict = { 'malicious': ['malware', 'malicious_packer'], 'trusted': ['application', 'library', 'interpreter', 'installer'], 'suspicious': ['administration_tool', 'packer'] } software_type_priorities = software_type_priorities_by_verdict.get(verdict) if software_type_priorities: main_family, gene_count = get_analysis_family(analysis, software_type_priorities, should_use_largest_families) if verdict in ('malicious', 'suspicious'): iocs = analysis.iocs dynamic_ttps = analysis.dynamic_ttps related_samples = [sub_analysis.get_account_related_samples(wait=True) for sub_analysis in analysis.get_sub_analyses()] if related_samples: related_samples_unique_count = len({analysis['analysis']['sha256'] for analysis in itertools.chain.from_iterable( sample.result['related_samples'] for sample in related_samples if sample is not None)}) return { 'verdict': verdict, 'sub_verdict': sub_verdict, 'analysis_url': analysis_url, 'main_family': main_family, 'gene_count': gene_count, 'iocs': iocs, 'dynamic_ttps': dynamic_ttps, 'related_samples_unique_count': related_samples_unique_count }
def test_send_analysis_by_file_and_get_report(self): # Arrange with responses.RequestsMock() as mock: mock.add('POST', url=self.full_url + '/analyze', status=201, json={'result_url': 'a/sd/asd'}) mock.add('GET', url=self.full_url + '/analyses/asd', status=200, json={ 'result': 'report', 'status': 'succeeded' }) analysis = FileAnalysis(file_path='a') with patch(self.patch_prop, mock_open(read_data='data')): # Act analysis.send(wait=True) # Assert self.assertEqual(analysis.status, consts.AnalysisStatusCode.FINISH) self.assertEqual(analysis.result(), 'report')
def test_analysis_get_report_for_not_finish_analyze_raise_error(self): # Arrange analysis = FileAnalysis(file_hash='a') # Act + Assert with self.assertRaises(errors.ReportDoesNotExistError): analysis.result()
def send_file_with_wait(file_path): api.set_global_api('<api_key>') analysis = FileAnalysis(file_path=file_path) analysis.send(wait=True) pprint(analysis.result())
def send_file_without_wait(file_path): api.set_global_api('<api_key>') analysis = FileAnalysis(file_path=file_path) analysis.send() analysis.wait_for_completion() pprint(analysis.result())
def get_analysis_summary(analysis: FileAnalysis, no_emojis: bool = False, short: bool = False, use_hash_link=False) -> str: result = analysis.result() metadata = analysis.get_root_analysis().metadata verdict = result['verdict'].lower() sub_verdict = result['sub_verdict'].lower() emoji = '' note = _get_title(short) if not no_emojis: emoji = get_emoji(verdict) if verdict == 'malicious': main_family, gene_count = get_analysis_family(analysis, []) elif verdict == 'trusted': main_family, gene_count = get_analysis_family(analysis, ['application', 'library', 'interpreter', 'installer']) elif verdict == 'suspicious': main_family, gene_count = get_analysis_family(analysis, ['administration_tool', 'packer']) else: main_family = None gene_count = None note = f'{note}{emoji} {verdict.capitalize()}' if verdict in ('suspicious', 'unknown'): note = f'{note} - {sub_verdict.replace("_", " ").title()}' if main_family: note = f'{note} - {main_family}' if gene_count and not short: note = f'{note} ({gene_count} shared code genes)' if use_hash_link: analysis_url = f"{ANALYZE_URL}/files/{result['sha256']}?private=true" else: analysis_url = result['analysis_url'] if short: return f'{note} > {analysis_url}' note = f'{note}\n\nSize: {human_readable_size(metadata["size_in_bytes"])}\n' if 'file_type' in metadata: note = f'{note}File type: {metadata["file_type"]}\n' if verdict in ('malicious', 'suspicious'): iocs = analysis.iocs if iocs: iocs_count = 0 files = iocs.get('files') network = iocs.get('network') if files: iocs_count += len(files) if network: iocs_count += len(network) if iocs_count > 1: note = f'{note}IOCs: {iocs_count} Indicators\n' if analysis.dynamic_ttps: note = f'{note}TTPs: {len(analysis.dynamic_ttps)} techniques\n' related_samples = [sub_analysis.get_account_related_samples(wait=True) for sub_analysis in analysis.get_sub_analyses()] if related_samples: related_samples_unique_count = len({analysis['analysis']['sha256'] for analysis in itertools.chain.from_iterable( sample.result['related_samples'] for sample in related_samples if sample is not None)}) note = f'{note}Similar previous uploads: {related_samples_unique_count} files \n' note = (f'{note}\nFull report:\n' f'{"" if no_emojis else get_emoji("result_url")} {analysis_url}') return note
def analysis_by_hash_with_wait(file_hash: str): api.set_global_api('<api_key>') analysis = FileAnalysis(file_hash=file_hash) analysis.send(wait=True) pprint(analysis.result())
def analysis_by_hash_without_wait(file_hash: str): api.set_global_api('<api_key>') analysis = FileAnalysis(file_hash=file_hash) analysis.send() analysis.wait_for_completion() pprint(analysis.result())
def analysis_by_hash_with_wait_timeout(file_hash: str): api.set_global_api('<api_key>') analysis = FileAnalysis(file_hash=file_hash) analysis.send(wait=True, wait_timeout=datetime.timedelta(minutes=1)) pprint(analysis.result())
def send_analysis(analysis: FileAnalysis): analysis.send(wait=True) return analysis.result()