From f1134fe93f11eb42a1a188ab33420567463d04dc Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Fri, 18 Jul 2025 09:12:45 +0200 Subject: [PATCH 1/2] [client] Introduce fields product on software and first_seen_active on vulnerability (#941) --- examples/get_indicators_of_campaign.py | 38 +++++++++++++++++++ .../entities/opencti_stix_cyber_observable.py | 17 +++++++++ pycti/entities/opencti_vulnerability.py | 14 +++++++ ...pencti_stix_cyber_observable_properties.py | 2 + 4 files changed, 71 insertions(+) create mode 100644 examples/get_indicators_of_campaign.py diff --git a/examples/get_indicators_of_campaign.py b/examples/get_indicators_of_campaign.py new file mode 100644 index 000000000..edce20eb8 --- /dev/null +++ b/examples/get_indicators_of_campaign.py @@ -0,0 +1,38 @@ +# coding: utf-8 +import datetime + +from pycti import OpenCTIApiClient + +# Variables +api_url = "http://opencti:4000" +api_token = "bfa014e0-e02e-4aa6-a42b-603b19dcf159" + +# OpenCTI initialization +opencti_api_client = OpenCTIApiClient(api_url, api_token) + +# Create the Intrusion Set +opencti_api_client.intrusion_set.create( + name="APT28", + description="Evil hackers", + first_seen=datetime.date.today().strftime("%Y-%m-%dT%H:%M:%S+00:00"), + last_seen=datetime.date.today().strftime("%Y-%m-%dT%H:%M:%S+00:00"), + update=True, +) + +# Get the intrusion set APT28 +intrusion_set = opencti_api_client.intrusion_set.read( + filters={ + "mode": "and", + "filters": [{"key": "name", "values": ["APT28"]}], + "filterGroups": [], + } +) + +# Get the relations from APT28 to malwares +stix_relations = opencti_api_client.stix_core_relationship.list( + fromId=intrusion_set["id"], toTypes=["Malware"] +) + +# Print +for stix_relation in stix_relations: + print("[" + stix_relation["to"]["stix_id"] + "] " + stix_relation["to"]["name"]) diff --git a/pycti/entities/opencti_stix_cyber_observable.py b/pycti/entities/opencti_stix_cyber_observable.py index cb69cece1..d0c1b794e 100644 --- a/pycti/entities/opencti_stix_cyber_observable.py +++ b/pycti/entities/opencti_stix_cyber_observable.py @@ -809,6 +809,18 @@ def create(self, **kwargs): ), } elif type == "Software": + if ( + "x_opencti_product" not in observable_data + and self.opencti.get_attribute_in_extension( + "x_opencti_product", observable_data + ) + is not None + ): + observable_data["x_opencti_product"] = ( + self.opencti.get_attribute_in_extension( + "x_opencti_product", observable_data + ) + ) input_variables["Software"] = { "name": ( observable_data["name"] if "name" in observable_data else None @@ -832,6 +844,11 @@ def create(self, **kwargs): if "version" in observable_data else None ), + "x_opencti_product": ( + observable_data["x_opencti_product"] + if "x_opencti_product" in observable_data + else None + ), } elif type == "Url": input_variables["Url"] = { diff --git a/pycti/entities/opencti_vulnerability.py b/pycti/entities/opencti_vulnerability.py index b9b118012..2d6a55d05 100644 --- a/pycti/entities/opencti_vulnerability.py +++ b/pycti/entities/opencti_vulnerability.py @@ -150,6 +150,7 @@ def __init__(self, opencti): x_opencti_cvss_v4_availability_impact_s x_opencti_cvss_v4_exploit_maturity x_opencti_cwe + x_opencti_first_seen_active x_opencti_cisa_kev x_opencti_epss_score x_opencti_epss_percentile @@ -452,6 +453,7 @@ def create(self, **kwargs): x_opencti_epss_score = kwargs.get("x_opencti_epss_score", None) x_opencti_epss_percentile = kwargs.get("x_opencti_epss_percentile", None) x_opencti_score = kwargs.get("x_opencti_score", None) + x_opencti_first_seen_active = kwargs.get("x_opencti_first_seen_active", None) x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None) granted_refs = kwargs.get("objectOrganization", None) x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None) @@ -538,6 +540,7 @@ def create(self, **kwargs): "x_opencti_epss_score": x_opencti_epss_score, "x_opencti_epss_percentile": x_opencti_epss_percentile, "x_opencti_score": x_opencti_score, + "x_opencti_first_seen_active": x_opencti_first_seen_active, "x_opencti_stix_ids": x_opencti_stix_ids, "x_opencti_workflow_id": x_opencti_workflow_id, "update": update, @@ -867,6 +870,12 @@ def import_from_stix2(self, **kwargs): stix_object["x_opencti_score"] = ( self.opencti.get_attribute_in_extension("score", stix_object) ) + if "x_opencti_first_seen_active" not in stix_object: + stix_object["x_opencti_first_seen_active"] = ( + self.opencti.get_attribute_in_extension( + "first_seen_active", stix_object + ) + ) return self.create( stix_id=stix_object["id"], @@ -1158,6 +1167,11 @@ def import_from_stix2(self, **kwargs): if "x_opencti_score" in stix_object else None ), + x_opencti_first_seen_active=( + stix_object["x_opencti_first_seen_active"] + if "x_opencti_first_seen_active" in stix_object + else None + ), update=update, ) else: diff --git a/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py b/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py index 70f95a289..176de35f7 100644 --- a/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py +++ b/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py @@ -215,6 +215,7 @@ languages vendor version + x_opencti_product } ... on Url { value @@ -519,6 +520,7 @@ languages vendor version + x_opencti_product } ... on Url { value From f8ec409afeb0887709800e9ed1cdde71ad525996 Mon Sep 17 00:00:00 2001 From: Samuel Hassine Date: Fri, 18 Jul 2025 09:24:05 +0200 Subject: [PATCH 2/2] [client] Introduce fields product on software and first_seen_active on vulnerability (#941) --- examples/get_indicators_of_campaign.py | 38 -------------------------- 1 file changed, 38 deletions(-) delete mode 100644 examples/get_indicators_of_campaign.py diff --git a/examples/get_indicators_of_campaign.py b/examples/get_indicators_of_campaign.py deleted file mode 100644 index edce20eb8..000000000 --- a/examples/get_indicators_of_campaign.py +++ /dev/null @@ -1,38 +0,0 @@ -# coding: utf-8 -import datetime - -from pycti import OpenCTIApiClient - -# Variables -api_url = "http://opencti:4000" -api_token = "bfa014e0-e02e-4aa6-a42b-603b19dcf159" - -# OpenCTI initialization -opencti_api_client = OpenCTIApiClient(api_url, api_token) - -# Create the Intrusion Set -opencti_api_client.intrusion_set.create( - name="APT28", - description="Evil hackers", - first_seen=datetime.date.today().strftime("%Y-%m-%dT%H:%M:%S+00:00"), - last_seen=datetime.date.today().strftime("%Y-%m-%dT%H:%M:%S+00:00"), - update=True, -) - -# Get the intrusion set APT28 -intrusion_set = opencti_api_client.intrusion_set.read( - filters={ - "mode": "and", - "filters": [{"key": "name", "values": ["APT28"]}], - "filterGroups": [], - } -) - -# Get the relations from APT28 to malwares -stix_relations = opencti_api_client.stix_core_relationship.list( - fromId=intrusion_set["id"], toTypes=["Malware"] -) - -# Print -for stix_relation in stix_relations: - print("[" + stix_relation["to"]["stix_id"] + "] " + stix_relation["to"]["name"])