cve-enrichment
Enriches CVE indicators with reputation data from multiple integrations and outputs a consolidated CVEEnrichment object. This script exclusively supports indicators of type CVE and will automatically create the indicator in TIM if it is not already exists.
python · Aggregated Scripts
Details
| ID | cve-enrichment |
|---|---|
| Language | python |
| From Version | 8.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Tags | basescript |
README
Enriches CVE indicators with reputation data from multiple integrations and outputs a consolidated CVEEnrichment object. This script exclusively supports indicators of type CVE and will automatically create the indicator in TIM if it is not already exists.
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.
- CVEEnrichment-Test
Inputs
| Argument Name | Description |
|---|---|
| cve_list | A comma-separated list of CVEs to enrich. |
| external_enrichment | Whether to call external integrations for enrichment. - ‘true’: enrich using enabled external integrations (e.g., CIRCL CVE Search, CVE Search v2). - ‘false’: use only existing TIM data; skip external integrations. If the ‘brands’ argument is provided, this flag is ignored and enrichment is run only on the brands provided. |
| verbose | Whether to retrieve a human-readable entry for every command; if false, only the final result is summarized and error entries are suppressed. |
| brands | A list of integration brands to run enrichment against. Example: `“CIRCL CVE Search, CVE Search 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. To see the available brands for the `cve` command, run: `!ProvidesCommand command=cve`. |
| additional_fields | When set to true, the output will also include an `AdditionalFields` object for each of the indicator result. `AdditionalFields` contains all fields returned by TIM or the integrations that are not part of the standard output keys: `ID`, `Brand`, `CVSS`, `Description`, `Published`. When set to false, only the standard keys are returned. |
Outputs
| Path | Description | Type |
|---|---|---|
| CVEEnrichment.Value | The CVE. | string |
| CVEEnrichment.MaxCVSS | The max CVSS of the indicator. | number |
| CVEEnrichment.MaxCVSSRating | The max CVSS rating of the indicator. | string |
| CVEEnrichment.Results | List of all indicators found for the CVE. | array |
| CVEEnrichment.Status | The status of the indicator. | string |
| CVEEnrichment.Results.ID | The ID of the indicator. | string |
| CVEEnrichment.Results.Brand | The brand of the indicator. | string |
| CVEEnrichment.Results.CVSS | The CVSS of the indicator. | number |
| CVEEnrichment.Results.Description | The description of the indicator. | string |
| CVEEnrichment.Results.Published | The published date of the indicator. | string |
| CVEEnrichment.Results.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 |
| CVEEnrichment.Results.ModifiedTime | The time the indicator was last modified. | Date |
| CVEEnrichment.Results.AdditionalFields | All fields extracted from the indicator other then the main keys (“ID”, “Brand”, “CVSS”, “Description”, “Published”, “CVSS”). | Object |
| CVEEnrichment.Results.AdditionalFields.Relationships.EntityA | The source of the relationship. | string |
| CVEEnrichment.Results.AdditionalFields.Relationships.EntityB | The destination of the relationship. | string |
| CVEEnrichment.Results.AdditionalFields.Relationships.Relationship | The name of the relationship. | string |
| CVEEnrichment.Results.AdditionalFields.Relationships.EntityAType | The type of the source of the relationship. | string |
| CVEEnrichment.Results.AdditionalFields.Relationships.EntityBType | The type of the destination of the relationship. | string |
| CVEEnrichment.Results.AdditionalFields.Modified | The timestamp of when the CVE was last modified. | Date |
import json import demistomock as demisto from CVEEnrichment import cve_enrichment_script def util_load_json(path: str): with open(path, encoding="utf-8") as f: return json.load(f) # End-to-end: TIM + enrichIndicators (batch data from file) def test_cve_enrichment_script_end_to_end_with_batch_file(mocker): r""" Given: - Two CVEs. - TIM returns pages from mock_cve_tim_results.json (with reliability + manual edit + modifiedTime). - Batches: * createNewIndicator (no-op) * enrichIndicators per CVE (DBotScore only for CVE-2024-0001 / brand1; not surfaced by mapping) When: - cve_enrichment_script runs end-to-end (external_enrichment=True). Then: - CVEEnrichment contains both CVEs. - For CVE-2024-0001: * Results include TIM + brand1 + brand2 (3 items). - TIM row has no Status/ModifiedTime (popped to top-level). * TIMScore == 2; Status == "Manual"; ModifiedTime == fixture value. - For CVE-2023-9999: * Results include TIM + brand3 (2 items), with brand3 Reliability == "Low". - We don't assert DBotScore path (not mapped in outputs). """ tim_pages = util_load_json("test_data/mock_cve_tim_results.json")["pages"] batch_data = util_load_json("test_data/mock_cve_batch_results.json") cve_list = ["CVE-2024-0001", "CVE-2023-9999"] mocker.patch.object(demisto, "args", return_value={"cve_list": ",".join(cve_list)}) # extractIndicators -> validates input mocker.patch( "AggregatedCommandApiModule.execute_command", return_value=[{"EntryContext": {"ExtractedIndicators": {"CVE": cve_list}}}], ) # TIM search via IndicatorsSearcher 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 brands (external enrich runs for these) mocker.patch.object( demisto, "getModules", return_value={ "m1": {"state": "active", "brand": "brand1"}, "m2": {"state": "active", "brand": "brand2"}, "m3": {"state": "active", "brand": "brand3"}, }, ) # Helpers def _wrap_all_in_one_command(entries, hr=""): return [[(e, hr, "") for e in entries]] # Batch executor mock → map fixtures to command batches def _fake_execute_list_of_batches(self, list_of_batches, brands_to_run=None, verbose=False): out = [] # ----- Batch 1: createNewIndicator ----- b1_entries = batch_data["batch1_createNewIndicator"] out.append(_wrap_all_in_one_command(b1_entries)) # ----- Batch 2: enrichIndicators ----- enrich_entries = batch_data["batch2_enrichIndicators"] out.append(_wrap_all_in_one_command(enrich_entries)) return out mocker.patch("AggregatedCommandApiModule.BatchExecutor.execute_list_of_batches", _fake_execute_list_of_batches) # Act res = cve_enrichment_script( cve_list=cve_list, external_enrichment=True, verbose=True, enrichment_brands=["brand1", "brand2", "brand3"], additional_fields=False, ) outputs = res.outputs # CVEEnrichment indicators key = "CVEEnrichment(val.Value && val.Value == obj.Value)" enrichment_list = outputs.get(key, []) enrichment_map = {item["Value"]: item for item in enrichment_list} assert set(enrichment_map.keys()) == set(cve_list) cve1 = enrichment_map["CVE-2024-0001"] # Results contains TIM + vendor rows assert {r.get("Brand") for r in cve1["Results"]} == {"TIM", "brand1", "brand2"} assert len(cve1["Results"]) == 3 # TIM row present but Status/ModifiedTime popped to top-level tim_row = next(r for r in cve1["Results"] if r["Brand"] == "TIM") assert "Status" not in tim_row assert "ModifiedTime" not in tim_row # Vendor reliabilities b1 = next(r for r in cve1["Results"] if r["Brand"] == "brand1") assert b1.get("Reliability") == "High" b2 = next(r for r in cve1["Results"] if r["Brand"] == "brand2") assert b2.get("Reliability") == "Medium" # TIM summarization assert cve1.get("Status") == "Manual" assert cve1.get("ModifiedTime") == "2025-09-01T00:00:00Z" assert cve1.get("TIMCVSS") == 7.5 # Second CVE: TIM + brand3, reliability Low cve2 = enrichment_map["CVE-2023-9999"] assert {r.get("Brand") for r in cve2["Results"]} == {"TIM", "brand3"} b3 = next(r for r in cve2["Results"] if r["Brand"] == "brand3") assert b3.get("Reliability") == "Low" # TIM summarization assert cve2.get("Status") is None assert cve2.get("ModifiedTime") is None assert cve2.get("TIMCVSS") == 1.2