Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preparing release 6.4.0 #790

Merged
merged 10 commits into from
Nov 18, 2024
31 changes: 31 additions & 0 deletions pycti/api/opencti_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ def set_playbook_id_header(self, playbook_id):
def set_event_id(self, event_id):
self.request_headers["opencti-event-id"] = event_id

def set_draft_id(self, draft_id):
self.request_headers["opencti-draft-id"] = draft_id

def set_synchronized_upsert_header(self, synchronized):
self.request_headers["synchronized-upsert"] = (
"true" if synchronized is True else "false"
Expand Down Expand Up @@ -666,6 +669,34 @@ def upload_file(self, **kwargs):
self.app_logger.error("[upload] Missing parameter: file_name")
return None

def create_draft(self, **kwargs):
"""create a draft in OpenCTI API
:param `**kwargs`: arguments for file name creating draft (required: `draft_name`)
:return: returns the query response for the draft creation
:rtype: id
"""

draft_name = kwargs.get("draft_name", None)
entity_id = kwargs.get("entity_id", None)

if draft_name is not None:
self.app_logger.info("Creating a draft.")
query = """
mutation draftWorkspaceAdd($input: DraftWorkspaceAddInput!) {
draftWorkspaceAdd(input: $input) {
id
}
}
"""
queryResult = self.query(
query,
{"input": {"name": draft_name, "entity_id": entity_id}},
)
return queryResult["data"]["draftWorkspaceAdd"]["id"]
else:
self.app_logger.error("[create_draft] Missing parameter: draft_name")
return None

def upload_pending_file(self, **kwargs):
"""upload a file to OpenCTI API

Expand Down
43 changes: 35 additions & 8 deletions pycti/connector/opencti_connector_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,18 @@ def _data_handler(self, json_data) -> None:
event_data = json_data["event"]
entity_id = event_data.get("entity_id")
entity_type = event_data.get("entity_type")
validation_mode = event_data.get("validation_mode", "workbench")
# Set the API headers
work_id = json_data["internal"]["work_id"]
internal_data = json_data["internal"]
work_id = internal_data["work_id"]
draft_id = internal_data.get("draft_id", "")
self.helper.work_id = work_id

self.helper.validation_mode = validation_mode
self.helper.draft_id = draft_id
self.helper.api.set_draft_id(draft_id)
self.helper.api_impersonate.set_draft_id(draft_id)

self.helper.playbook = None
self.helper.enrichment_shared_organizations = None
if self.helper.connect_type == "INTERNAL_ENRICHMENT":
Expand Down Expand Up @@ -952,6 +960,8 @@ def __init__(self, config: Dict, playbook_compatible=False) -> None:
"Connector registered with ID", {"id": self.connect_id}
)
self.work_id = None
self.validation_mode = "workbench"
self.draft_id = None
self.playbook = None
self.enrichment_shared_organizations = None
self.connector_id = connector_configuration["id"]
Expand Down Expand Up @@ -1550,6 +1560,7 @@ def send_stix2_bundle(self, bundle: str, **kwargs) -> list:
"""send a stix2 bundle to the API

:param work_id: a valid work id
:param draft_id: a draft context to send the bundle to
:param bundle: valid stix2 bundle
:type bundle:
:param entities_types: list of entities, defaults to None
Expand All @@ -1563,6 +1574,8 @@ def send_stix2_bundle(self, bundle: str, **kwargs) -> list:
:rtype: list
"""
work_id = kwargs.get("work_id", self.work_id)
validation_mode = kwargs.get("validation_mode", self.validation_mode)
draft_id = kwargs.get("draft_id", self.draft_id)
entities_types = kwargs.get("entities_types", None)
update = kwargs.get("update", False)
event_version = kwargs.get("event_version", None)
Expand Down Expand Up @@ -1627,14 +1640,23 @@ def send_stix2_bundle(self, bundle: str, **kwargs) -> list:
# Upload workbench in case of pending validation
if not file_name and work_id:
file_name = f"{work_id}.json"

if self.connect_validate_before_import and not bypass_validation and file_name:
self.api.upload_pending_file(
file_name=file_name,
data=bundle,
mime_type="application/json",
entity_id=entity_id,
)
return []
if validation_mode == "workbench":
self.api.upload_pending_file(
file_name=file_name,
data=bundle,
mime_type="application/json",
entity_id=entity_id,
)
return []
elif validation_mode == "draft" and not draft_id:
draft_id = self.api.create_draft(
draft_name=file_name, entity_id=entity_id
)
if not draft_id:
self.connector_logger.error("Draft couldn't be created")
return []

# If directory setup, write the bundle to the target directory
if bundle_send_to_directory and bundle_send_to_directory_path is not None:
Expand Down Expand Up @@ -1749,6 +1771,7 @@ def send_stix2_bundle(self, bundle: str, **kwargs) -> list:
entities_types=entities_types,
sequence=sequence,
update=update,
draft_id=draft_id,
)
channel.close()
pika_connection.close()
Expand All @@ -1774,11 +1797,14 @@ def _send_bundle(self, channel, bundle, **kwargs) -> None:
:type entities_types: list, optional
:param update: whether to update data in the database, defaults to False
:type update: bool, optional
:param draft_id: if draft_id is set, bundle must be set in draft context
:type draft_id:
"""
work_id = kwargs.get("work_id", None)
sequence = kwargs.get("sequence", 0)
update = kwargs.get("update", False)
entities_types = kwargs.get("entities_types", None)
draft_id = kwargs.get("draft_id", None)

if entities_types is None:
entities_types = []
Expand All @@ -1800,6 +1826,7 @@ def _send_bundle(self, channel, bundle, **kwargs) -> None:
"utf-8"
),
"update": update,
"draft_id": draft_id,
}
if work_id is not None:
message["work_id"] = work_id
Expand Down
8 changes: 8 additions & 0 deletions pycti/entities/indicator/opencti_indicator_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
x_opencti_score
x_opencti_detection
x_opencti_main_observable_type
x_opencti_observable_values {
type
value
}
x_mitre_platforms
observables {
edges {
Expand Down Expand Up @@ -220,6 +224,10 @@
x_opencti_score
x_opencti_detection
x_opencti_main_observable_type
x_opencti_observable_values {
type
value
}
x_mitre_platforms
observables {
edges {
Expand Down
8 changes: 6 additions & 2 deletions pycti/entities/opencti_attack_pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,19 @@ def __init__(self, opencti):

@staticmethod
def generate_id(name, x_mitre_id=None):
name = name.lower().strip()
if x_mitre_id is not None:
data = {"x_mitre_id": x_mitre_id}
else:
data = {"name": name}
data = {"name": name.lower().strip()}
data = canonicalize(data, utf8=False)
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "attack-pattern--" + id

@staticmethod
def generate_id_from_data(data):
external_id = data.get("x_mitre_id") or data.get("x_opencti_external_id")
return AttackPattern.generate_id(data.get("name"), external_id)

"""
List Attack-Pattern objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "campaign--" + id

@staticmethod
def generate_id_from_data(data):
return Campaign.generate_id(data["name"])

"""
List Campaign objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_case_incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ def generate_id(name, created):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "case-incident--" + id

@staticmethod
def generate_id_from_data(data):
return CaseIncident.generate_id(data["name"], data["created"])

"""
List Case Incident objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_case_rfi.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ def generate_id(name, created):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "case-rfi--" + id

@staticmethod
def generate_id_from_data(data):
return CaseRfi.generate_id(data["name"], data["created"])

"""
List Case Rfi objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_case_rft.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ def generate_id(name, created):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "case-rft--" + id

@staticmethod
def generate_id_from_data(data):
return CaseRft.generate_id(data["name"], data["created"])

"""
List Case Rft objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "channel--" + id

@staticmethod
def generate_id_from_data(data):
return Channel.generate_id(data["name"])

"""
List Channel objects

Expand Down
7 changes: 5 additions & 2 deletions pycti/entities/opencti_course_of_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,18 @@ def __init__(self, opencti):

@staticmethod
def generate_id(name, x_mitre_id=None):
name = name.lower().strip()
if x_mitre_id is not None:
data = {"x_mitre_id": x_mitre_id}
else:
data = {"name": name}
data = {"name": name.lower().strip()}
data = canonicalize(data, utf8=False)
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "course-of-action--" + id

@staticmethod
def generate_id_from_data(data):
return CourseOfAction.generate_id(data.get("name"), data.get("x_mitre_id"))

"""
List Course-Of-Action objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_data_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "data-component--" + id

@staticmethod
def generate_id_from_data(data):
return DataComponent.generate_id(data["name"])

"""
List Data-Component objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "data-source--" + id

@staticmethod
def generate_id_from_data(data):
return DataSource.generate_id(data["name"])

"""
List Data-Source objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "event--" + id

@staticmethod
def generate_id_from_data(data):
return Event.generate_id(data["name"])

"""
List Event objects

Expand Down
6 changes: 6 additions & 0 deletions pycti/entities/opencti_external_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ def generate_id(url=None, source_name=None, external_id=None):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "external-reference--" + id

@staticmethod
def generate_id_from_data(data):
return ExternalReference.generate_id(
data.get("url"), data.get("source_name"), data.get("external_id")
)

"""
List External-Reference objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ def generate_id(name):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "feedback--" + id

@staticmethod
def generate_id_from_data(data):
return Feedback.generate_id(data["name"])

"""
List Feedback objects

Expand Down
11 changes: 9 additions & 2 deletions pycti/entities/opencti_grouping.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,16 +396,23 @@ def __init__(self, opencti):
"""

@staticmethod
def generate_id(name, context, created):
def generate_id(name, context, created=None):
name = name.lower().strip()
context = context.lower().strip()
if isinstance(created, datetime.datetime):
created = created.isoformat()
data = {"name": name, "context": context, "created": created}
if created is None:
data = {"name": name, "context": context}
else:
data = {"name": name, "context": context, "created": created}
data = canonicalize(data, utf8=False)
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "grouping--" + id

@staticmethod
def generate_id_from_data(data):
return Grouping.generate_id(data["name"], data["context"], data["created"])

"""
List Grouping objects

Expand Down
7 changes: 5 additions & 2 deletions pycti/entities/opencti_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,15 @@ def __init__(self, opencti):

@staticmethod
def generate_id(name, identity_class):
name = name.lower().strip()
data = {"name": name, "identity_class": identity_class}
data = {"name": name.lower().strip(), "identity_class": identity_class.lower()}
data = canonicalize(data, utf8=False)
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "identity--" + id

@staticmethod
def generate_id_from_data(data):
return Identity.generate_id(data["name"], data["identity_class"])

"""
List Identity objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ def generate_id(name, created):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "incident--" + id

@staticmethod
def generate_id_from_data(data):
return Incident.generate_id(data["name"], data["created"])

"""
List Incident objects

Expand Down
4 changes: 4 additions & 0 deletions pycti/entities/opencti_indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def generate_id(pattern):
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "indicator--" + id

@staticmethod
def generate_id_from_data(data):
return Indicator.generate_id(data["pattern"])

def list(self, **kwargs):
"""List Indicator objects

Expand Down
Loading