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

IDcve-enrichment
Languagepython
From Version8.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Tagsbasescript

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