file-enrichment
Enriches File indicators with reputation data from multiple integrations and outputs a consolidated FileEnrichment object. This script exclusively supports indicators of type File and will automatically create the indicator in TIM if it is not already exists. Note: The script runs core-get-hash-analytics-prevalence on SHA256 values only.
python · Aggregated Scripts
Details
| ID | file-enrichment |
|---|---|
| Language | python |
| From Version | 8.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Tags | basescript |
README
Enriches File indicators with reputation data from multiple integrations and outputs a consolidated FileEnrichment object. This script exclusively supports indicators of type File and will automatically create the indicator in TIM if it is not already exists. Note: The script runs core-get-hash-analytics-prevalence on SHA256 values only.
Note: This script is supported in Cortex XSOAR 8.0 and later, and in Cortex XSIAM.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | basescript |
| Cortex XSOAR Version | 8.0.0 |
Used In
This script is used in the following playbooks and scripts.
- FileEnrichment - Test
Inputs
| Argument Name | Description |
|---|---|
| file_hash | A comma-separated list of File hashes to enrich. Supported types are: MD5, SHA1, SHA256, and SHA512. |
| external_enrichment | “Whether to call external integrations for enrichment: - ‘true’: enrich using all enabled integrations (e.g., VirusTotal (API v3), AlienVault OTX v2). - ‘false’: Will enrich using only WildFire-v2 if available; skip external integrations. If the ‘brands’ argument is provided, this flag is ignored and enrichment is run only on the brands provided.” |
| verbose | Retrieve a human-readable entry for each command; if false, only the final result is summarized. |
| brands | A list of integration brands to run enrichment against. Example: `“AlienVault OTX v2”, “WildFire-v2”`. - If provided, only the selected brands are used. - If left empty, the script runs enrichment on all enabled integrations, depending on the `external_enrichment` flag. - In order to run core-get-hash-analytics-prevalence, add Cortex Core - IR to the brands list (will run only on SHA256 values). To see the available brands for the `file` command, run: `!ProvidesCommand command=file`. |
| additional_fields | When set to true, the output includes an `AdditionalFields` object for each of the indicator results. `AdditionalFields` contains all fields returned by TIM or the integrations that are not part of the standard output keys: `MD5`, `SHA1`, `SHA256`, `SHA512`, `CRC32`, `CTPH`, `SSDeep`, `ImpHash`, `SourceTimeStamp`, `Modified`, `Path`, `Size`, `FileExtension`, `AssociatedFileNames`, `Brand`, `Score`. When set to false, only the standard keys are returned. |
Outputs
| Path | Description | Type |
|---|---|---|
| FileEnrichment.Value | The File hash from the input Value. | String |
| FileEnrichment.Hashes.MD5 | The file MD5 hash if exists. | String |
| FileEnrichment.Hashes.SHA1 | The file SHA1 hash if exists. | String |
| FileEnrichment.Hashes.SHA256 | The file SHA256 hash if exists. | String |
| FileEnrichment.Hashes.SHA512 | The file SHA512 hash if exists. | String |
| FileEnrichment.Hashes.CRC32 | The file CRC32 hash if exists. | String |
| FileEnrichment.Hashes.CTPH | The file CTPH hash if exists. | String |
| FileEnrichment.Hashes.SSDeep | The file SSDeep hash if exists. | String |
| FileEnrichment.Hashes.ImpHash | The file ImpHash hash if exists. | String |
| FileEnrichment.MaxScore | The max score of all the indicators found. | Number |
| FileEnrichment.MaxVerdict | The max verdict of all the indicators found. | String |
| FileEnrichment.Results | List of all indicators found for the File. | Array |
| FileEnrichment.TIMScore | The TIM score of the File. | Number |
| FileEnrichment.Status | The status of the indicator: “Manual” if the score was changed manually, “Fresh” if modified within the last week, “Stale” if modified more than a week ago, and “None” if never modified. | String |
| FileEnrichment.ModifiedTime | The time the indicator was last modified. | Date |
| FileEnrichment.Results.Brand | The brand of the indicator. | String |
| FileEnrichment.Results.Score | The score of the indicator. | Number |
| FileEnrichment.Results.Verdict | The verdict of the indicator. | String |
| FileEnrichment.Results.DetectionEngines | The detection engines of the indicator. | Number |
| FileEnrichment.Results.PositiveDetections | The positive detections of the indicator. | Number |
| FileEnrichment.Results.MD5 | The file MD5 hash if exists. | String |
| FileEnrichment.Results.SHA1 | The file SHA1 hash if exists. | String |
| FileEnrichment.Results.SHA256 | The file SHA256 hash if exists. | String |
| FileEnrichment.Results.SHA512 | The file SHA512 hash if exists. | String |
| FileEnrichment.Results.CRC32 | The file CRC32 hash if exists. | String |
| FileEnrichment.Results.CTPH | The file CTPH hash if exists. | String |
| FileEnrichment.Results.SSDeep | The file SSDeep hash if exists. | String |
| FileEnrichment.Results.ImpHash | The file ImpHash hash if exists. | String |
| FileEnrichment.Results.Reliability | The reliability of the Brand. | String |
| FileEnrichment.Results.AdditionalFields.Name | The name of the file including its extension. | String |
| FileEnrichment.Results.AdditionalFields.EntryID | The identifier used to locate the file in the Incident War Room. | String |
| FileEnrichment.Results.AdditionalFields.Actor | The threat actor associated with the file, if applicable. | String |
| FileEnrichment.Results.AdditionalFields.behavior.details | A brief description of the behavior exhibited by the file. | String |
| FileEnrichment.Results.AdditionalFields.behavior.title | A brief description of the behavior exhibited by the file. | String |
| FileEnrichment.Results.AdditionalFields.Campaign | The identified campaign associated with the file, if applicable. | String |
| FileEnrichment.Results.AdditionalFields.CommunityNotes.note | Community-contributed notes regarding observations or findings related to the file. | String |
| FileEnrichment.Results.AdditionalFields.CommunityNotes.timestamp | The timestamp when the community note was added. | Date |
| FileEnrichment.Results.AdditionalFields.Company | The name of the company that released a binary. | String |
| FileEnrichment.Results.AdditionalFields.DigitalSignature.Publisher | The entity that issued the digital signature of the file. | String |
| FileEnrichment.Results.AdditionalFields.Extension | The file extension, indicating the type of file format, for example, ‘exe’. | String |
| FileEnrichment.Results.AdditionalFields.FeedRelatedIndicators.value | Shows other indicators associated with the file. | String |
| FileEnrichment.Results.AdditionalFields.FeedRelatedIndicators.type | Identifies the types of associated indicators. | String |
| FileEnrichment.Results.AdditionalFields.FeedRelatedIndicators.description | Describes the associated indicators providing context or relevance. | String |
| FileEnrichment.Results.AdditionalFields.FirstSeenBySource | The first time seen by the source brand. | Date |
| FileEnrichment.Results.AdditionalFields.GlobalPrevalence | The global prevalence of the file hash. | Number |
| FileEnrichment.Results.AdditionalFields.Hostname | The hostname of the device where the file was found. | String |
| FileEnrichment.Results.AdditionalFields.LastSeenBySource | The last time seed by the source brand. | Date |
| FileEnrichment.Results.AdditionalFields.Malicious.Vendor | Specifies the vendor that identified the file as malicious. | String |
| FileEnrichment.Results.AdditionalFields.Malicious.Description | For malicious files, the reason that the vendor made the decision. | Unknown |
| FileEnrichment.Results.AdditionalFields.Malicious.Detections | For malicious files, the total number of detections. | Unknown |
| FileEnrichment.Results.AdditionalFields.Malicious.TotalEngines | For malicious files, the total number of engines that checked the file hash. | Unknown |
| FileEnrichment.Results.AdditionalFields.VTVendors.EngineDetections | Number of VT vendors that flagged the file as malicious. | Unknown |
| FileEnrichment.Results.AdditionalFields.VTVendors.EngineVendors | VT vendors who flagged the file as malicious. | Unknown |
| FileEnrichment.Results.AdditionalFields.VTVendors.EngineDetectionNames | VT detection names that flagged the file as malicious. | Unknown |
| FileEnrichment.Results.AdditionalFields.MalwareFamily | Names the malware family associated with the file, if known. | String |
| FileEnrichment.Results.AdditionalFields.Organization | The organization to which the file is attributed. | String |
| FileEnrichment.Results.AdditionalFields.OrganizationFirstSeen | The date and time when the indicator was first seen in the organization. | Date |
| FileEnrichment.Results.AdditionalFields.OrganizationLastSeen | The date and time when the indicator was last seen in the organization. | Date |
| FileEnrichment.Results.AdditionalFields.OrganizationPrevalence | The number of times the indicator is detected in the organization. | Number |
| FileEnrichment.Results.AdditionalFields.ProductName | The file product name. | String |
| FileEnrichment.Results.AdditionalFields.Publications.source | Identifies the publishing source of an article relating to the file. | String |
| FileEnrichment.Results.AdditionalFields.Publications.title | Identifies the publishing source of an article relating to the file. | String |
| FileEnrichment.Results.AdditionalFields.Publications.link | Provides a hyperlink to the full article or publication for detailed information. | String |
| FileEnrichment.Results.AdditionalFields.Publications.timestamp | Publications.timestamp | Date |
| FileEnrichment.Results.AdditionalFields.Quarantined | Indicates whether the file has been quarantined to prevent potential harm. | Bool |
| FileEnrichment.Results.AdditionalFields.Relationships.EntityA | The initiating entity in a relationship involving the file. | String |
| FileEnrichment.Results.AdditionalFields.Relationships.EntityB | The recipient or target entity in a relationship involving the file. | String |
| FileEnrichment.Results.AdditionalFields.Relationships.Relationship | Defines the type or nature of the relationship between entities. | String |
| FileEnrichment.Results.AdditionalFields.Relationships.EntityAType | The type or classification of the initiating entity. | String |
| FileEnrichment.Results.AdditionalFields.Relationships.EntityBType | The type or classification of the recipient entity. | String |
| FileEnrichment.Results.AdditionalFields.Signature.Authentihash | The Authentihash, a cryptographic hash, used for verifying the file’s authenticity. | String |
| FileEnrichment.Results.AdditionalFields.Signature.Description | Describes the file signature data relevant to identification. | String |
| FileEnrichment.Results.AdditionalFields.Signature.FileVersion | Indicates the version number of the file. | String |
| FileEnrichment.Results.AdditionalFields.Signature.InternalName | The internal name of the file as designated by the creators. | String |
| FileEnrichment.Results.AdditionalFields.Signature.OriginalName | The original name of the file before any changes or renames. | String |
| FileEnrichment.Results.AdditionalFields.Tags | Tags assigned to the file for categorization or identification. | Array |
| FileEnrichment.Results.AdditionalFields.ThreatTypes | Threat types associated with the file. | Unknown |
| FileEnrichment.Results.AdditionalFields.TrafficLightProtocol | Specifies the TLP color designation suitable for handling the file. | String |
| FileEnrichment.Results.AdditionalFields.Type | The file type determined by libmagic. | String |
| Core.AnalyticsPrevalence.Hash.value | Whether the hash is prevalent or not. | Boolean |
| Core.AnalyticsPrevalence.Hash.data.global_prevalence.value | The global prevalence of the hash. | Number |
| Core.AnalyticsPrevalence.Hash.data.local_prevalence.value | The local prevalence of the hash. | Number |
| Core.AnalyticsPrevalence.Hash.data.prevalence.value | The prevalence of the hash. | Number |
import json import demistomock as demisto from FileEnrichment import file_enrichment_script def util_load_json(path: str): with open(path, encoding="utf-8") as f: return json.load(f) def test_file_enrichment_script_end_to_end_with_files(mocker): """ Given: - Two file hashes (both SHA256). - TIM file results from test_data/mock_file_tim_results.json. - Batch results from test_data/mock_file_batch_results.json (create + enrich + core-get-hash-analytics-prevalence). When: - file_enrichment_script runs end-to-end (external_enrichment=True). Then: - FileEnrichmentV2 contains both hashes. - For file1: * Results has 2 entries (TIM + WildFire-v2). * Hashes contain MD5 + SHA256. * TIMScore=3, MaxScore=3, MaxVerdict=Malicious. * Top-level Status == "Manual" (due to manuallyEditedFields.Score). * TIM row in Results has NO Status/ModifiedTime (popped to top-level). - For file2: * Results has 2 entries (TIM + WildFire-v2), reliability Low. """ # ---------- Load fixtures ---------- tim_pages = util_load_json("test_data/mock_file_tim_results.json")["pages"] batch_blob = util_load_json("test_data/mock_file_batch_results.json") file_list = [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", # file1 SHA256 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", # file2 SHA256 ] mocker.patch.object(demisto, "args", return_value={"file_hash": ",".join(file_list)}) # ---------- Mock execute_command ONLY for extractIndicators ---------- def extractIndicators_side_effect(cmd, args=None, extract_contents=False, fail_on_error=True): if cmd == "extractIndicators": return [{"EntryContext": {"ExtractedIndicators": {"File": file_list}}}] return [] mocker.patch("AggregatedCommandApiModule.execute_command", side_effect=extractIndicators_side_effect) class _MockSearcher: def __init__(self, pages): self.pages = pages def __iter__(self): return iter(self.pages) mocker.patch("AggregatedCommandApiModule.IndicatorsSearcher", return_value=_MockSearcher(tim_pages)) # ---------- Enabled modules/brands (BrandManager) ---------- mocker.patch.object( demisto, "getModules", return_value={ "wf": {"state": "active", "brand": "WildFire-v2"}, "core": {"state": "active", "brand": "Cortex Core - IR"}, }, ) mocker.patch("FileEnrichment.is_platform", return_value=False) # ---------- Mock BatchExecutor.execute_list_of_batches using JSON ---------- def _fake_execute_list_of_batches(self, list_of_batches, brands_to_run=None, verbose=False): out = [] create_items = list(batch_blob.get("createNewIndicator", [])) enrich_items = list(batch_blob.get("enrichIndicators", [])) core_items = list(batch_blob.get("coreGetHashAnalyticsPrevalence", [])) # Batch 0: CreateNewIndicatorsOnly batch0_cmds = list_of_batches[0] batch0_results = [] for _ in batch0_cmds: item = create_items.pop(0) if create_items else {"Type": 1, "EntryContext": {}} batch0_results.append([(item, "", "")]) out.append(batch0_results) # Batch 1: enrichIndicators + core-get-hash-analytics-prevalence # (a single prevalence command carries a list of SHA256 values) batch1_cmds = list_of_batches[1] batch1_results = [] for cmd in batch1_cmds: if cmd.name == "enrichIndicators": items = enrich_items or [{"Type": 1, "EntryContext": {}, "Metadata": {"brand": "WildFire-v2"}}] batch1_results.append([(e, "", "") for e in items]) elif cmd.name == "core-get-hash-analytics-prevalence": items = core_items or [ { "Type": 1, "EntryContext": {}, "Metadata": {"brand": "Cortex Core - IR"}, } ] batch1_results.append([(item, "", "") for item in items]) else: batch1_results.append([({"Type": 1, "EntryContext": {}}, "", "")]) out.append(batch1_results) return out mocker.patch("AggregatedCommandApiModule.BatchExecutor.execute_list_of_batches", _fake_execute_list_of_batches) # ---------- Act ---------- command_results = file_enrichment_script( file_list=file_list, external_enrichment=True, verbose=True, enrichment_brands=["WildFire-v2"], additional_fields=False, ) outputs = command_results.outputs # ---------- Assert: FileEnrichmentV2 indicators ---------- enrichment_key = "FileEnrichment(val.Value && val.Value == obj.Value)" enrichment_list = outputs.get(enrichment_key, []) assert len(enrichment_list) == 2 enrichment_map = {item["Value"]: item for item in enrichment_list} # In this scenario, Value will be the canonical TIM "value" (sha256) → same as file_list assert set(enrichment_map.keys()) == set(file_list) # ---- file1 assertions ---- f1 = enrichment_map[file_list[0]] brands_present_f1 = {r.get("Brand") for r in f1["Results"]} assert brands_present_f1 == {"TIM", "WildFire-v2"} assert len(f1["Results"]) == 2 # Hashes aggregated from TIM indicator hashes1 = f1.get("Hashes", {}) assert hashes1.get("MD5") is not None assert hashes1.get("SHA256") == file_list[0] # TIM row present but without Status/ModifiedTime (popped to top-level) tim_row_f1 = next(r for r in f1["Results"] if r["Brand"] == "TIM") assert "Status" not in tim_row_f1 assert "ModifiedTime" not in tim_row_f1 # Top-level scores & status assert f1["TIMScore"] == 3 assert f1["MaxScore"] == 3 assert f1["MaxVerdict"] == "Malicious" assert f1["Status"] == "Manual" # due to manuallyEditedFields.Score in TIM IOC # ---- file2 assertions ---- f2 = enrichment_map[file_list[1]] brands_present_f2 = {r.get("Brand") for r in f2["Results"]} assert brands_present_f2 == {"TIM", "WildFire-v2"} assert len(f2["Results"]) == 2 wf2 = next(r for r in f2["Results"] if r["Brand"] == "WildFire-v2") assert wf2["Score"] == 1 assert wf2.get("Reliability") == "Low" def _capture_built_commands(mocker, file_list, is_platform=False): """Helper: runs file_enrichment_script with mocks and returns the command batches exactly as they were built by file_enrichment_script, captured before any brand/type filtering is applied.""" mocker.patch.object(demisto, "args", return_value={"file_hash": ",".join(file_list)}) mocker.patch("FileEnrichment.is_platform", return_value=is_platform) def extractIndicators_side_effect(cmd, args=None, extract_contents=False, fail_on_error=True): if cmd == "extractIndicators": return [{"EntryContext": {"ExtractedIndicators": {"File": file_list}}}] return [] mocker.patch("AggregatedCommandApiModule.execute_command", side_effect=extractIndicators_side_effect) # Build a minimal TIM page so each input hash is "found" in TIM. This keeps the # enrichment flow on the success path (instead of the all-failed path which now # raises), while these tests only care about the commands that were built. def _build_ioc(file_hash): custom_fields = {"sha256": file_hash} if len(file_hash) == 64 else {"md5": file_hash} return { "value": file_hash, "score": 1, "modifiedTime": "2025-09-01T00:00:00Z", "CustomFields": custom_fields, } tim_pages = [{"iocs": [_build_ioc(file_hash) for file_hash in file_list]}] class _MockSearcher: def __init__(self, pages): self.pages = pages def __iter__(self): return iter(self.pages) mocker.patch("AggregatedCommandApiModule.IndicatorsSearcher", return_value=_MockSearcher(tim_pages)) mocker.patch.object( demisto, "getModules", return_value={ "wf": {"state": "active", "brand": "WildFire-v2"}, "core": {"state": "active", "brand": "Cortex Core - IR"}, }, ) captured = {} def _fake_prepare_commands_batches(self, external_enrichment=False): # Capture the commands as built (pre-filter) so the assertions are independent of # the brand/type filtering performed by the real prepare_commands_batches. captured["batches"] = self.commands return self.commands mocker.patch( "AggregatedCommandApiModule.ReputationAggregatedCommand.prepare_commands_batches", _fake_prepare_commands_batches, ) def _fake_execute_list_of_batches(self, list_of_batches, brands_to_run=None, verbose=False): return [[[({"Type": 1, "EntryContext": {}}, "", "")] for _ in batch] for batch in list_of_batches] mocker.patch("AggregatedCommandApiModule.BatchExecutor.execute_list_of_batches", _fake_execute_list_of_batches) file_enrichment_script( file_list=file_list, external_enrichment=True, verbose=False, enrichment_brands=["WildFire-v2"], additional_fields=False, ) return captured["batches"] def test_prevalence_command_omitted_when_no_sha256(mocker): """ Given: - A single MD5 hash (no SHA256) as input. When: - file_enrichment_script builds the command batches. Then: - The core-get-hash-analytics-prevalence command is NOT included. """ md5_hash = "d41d8cd98f00b204e9800998ecf8427e" batches = _capture_built_commands(mocker, [md5_hash]) all_command_names = [cmd.name for batch in batches for cmd in batch] assert "core-get-hash-analytics-prevalence" not in all_command_names def test_prevalence_command_included_when_sha256_present(mocker): """ Given: - A SHA256 hash as input. When: - file_enrichment_script builds the command batches. Then: - A single core-get-hash-analytics-prevalence command is included with the SHA256 value. """ sha256_hash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" batches = _capture_built_commands(mocker, [sha256_hash]) prevalence_cmds = [cmd for batch in batches for cmd in batch if cmd.name == "core-get-hash-analytics-prevalence"] assert len(prevalence_cmds) == 1 assert prevalence_cmds[0].args == {"sha256": [sha256_hash]} def test_file_enrichment_uses_builtin_command_on_platform(mocker): """ Given: - A SHA256 hash as input, running on the unified Cortex platform (is_platform True). When: - file_enrichment_script builds the command batches. Then: - The prevalence command is the built-in "getHashAnalyticsPrevalence". - Its command_type is CommandType.BUILTIN (not the legacy INTERNAL core command). """ from AggregatedCommandApiModule import CommandType sha256_hash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" batches = _capture_built_commands(mocker, [sha256_hash], is_platform=True) prevalence_cmds = [cmd for batch in batches for cmd in batch if cmd.name == "getHashAnalyticsPrevalence"] assert len(prevalence_cmds) == 1 assert prevalence_cmds[0].command_type == CommandType.BUILTIN assert "core-get-hash-analytics-prevalence" not in [cmd.name for batch in batches for cmd in batch]