Skip to content

Commit 47b8dcc

Browse files
author
Samuel Hassine
committed
[client] Handle simple cyber observable
1 parent 93c410d commit 47b8dcc

File tree

4 files changed

+138
-21
lines changed

4 files changed

+138
-21
lines changed

examples/import_stix2_file.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
opencti_api_client = OpenCTIApiClient(api_url, api_token)
1111

1212
# File to import
13-
file_to_import = "./enterprise-attack.json"
13+
file_to_import = "./test.json"
1414

1515
# Import the bundle
1616
opencti_api_client.stix2.import_bundle_from_file(file_to_import, True)

pycti/entities/opencti_stix_cyber_observable.py

+101-5
Original file line numberDiff line numberDiff line change
@@ -407,19 +407,30 @@ def read(self, **kwargs):
407407
"""
408408

409409
def create(self, **kwargs):
410-
observable_data = kwargs.get("observableData", None)
410+
observable_data = kwargs.get("observableData", {})
411+
simple_observable_key = kwargs.get("simple_observable_key", None)
412+
simple_observable_value = kwargs.get("simple_observable_value", None)
411413
created_by = kwargs.get("createdBy", None)
412414
object_marking = kwargs.get("objectMarking", None)
413415
object_label = kwargs.get("objectLabel", None)
414416
external_references = kwargs.get("externalReferences", None)
415417
update = kwargs.get("update", False)
416418

419+
no_global_attributes = ["File.MD5", "File.SHA-1", "File-SHA256", "File.SHA-512"]
417420
create_indicator = (
418421
observable_data["x_opencti_create_indicator"]
419422
if "x_opencti_create_indicator" in observable_data
420423
else kwargs.get("createIndicator", False)
421424
)
422-
type = observable_data["type"].title()
425+
attribute = None
426+
if simple_observable_key is not None:
427+
key_split = simple_observable_key.split(".")
428+
type = key_split[0]
429+
attribute = key_split[1]
430+
else:
431+
type = (
432+
observable_data["type"].title() if "type" in observable_data else None
433+
)
423434
if type.lower() == "file":
424435
type = "StixFile"
425436
elif type.lower() == "ipv4-addr":
@@ -530,6 +541,10 @@ def create(self, **kwargs):
530541
else None,
531542
"rir": observable_data["rir"] if "rir" in observable_data else None,
532543
}
544+
if simple_observable_value is not None:
545+
input_variables["AutonomousSystem"][
546+
attribute
547+
] = simple_observable_value
533548
elif type == "Directory":
534549
input_variables["Directory"] = {
535550
"path": observable_data["path"],
@@ -546,15 +561,21 @@ def create(self, **kwargs):
546561
if "atime" in observable_data
547562
else None,
548563
}
564+
if attribute is not None:
565+
input_variables["Directory"][attribute] = simple_observable_value
549566
elif type == "Domain-Name":
550567
input_variables["DomainName"] = {"value": observable_data["value"]}
568+
if attribute is not None:
569+
input_variables["DomainName"][attribute] = simple_observable_value
551570
elif type == "Email-Addr":
552571
input_variables["EmailAddr"] = {
553572
"value": observable_data["value"],
554573
"display_name": observable_data["display_name"]
555574
if "display_name" in observable_data
556575
else None,
557576
}
577+
if simple_observable_value is not None:
578+
input_variables["EmailAddr"][attribute] = simple_observable_value
558579
elif type == "Email-Message":
559580
input_variables["EmailMessage"] = {
560581
"is_multipart": observable_data["is_multipart"]
@@ -579,6 +600,8 @@ def create(self, **kwargs):
579600
if "body" in observable_data
580601
else None,
581602
}
603+
if attribute is not None:
604+
input_variables["EmailMessage"][attribute] = simple_observable_value
582605
elif type == "Email-Mime-Part-Type":
583606
input_variables["EmailMimePartType"] = {
584607
"body": observable_data["body"]
@@ -591,6 +614,10 @@ def create(self, **kwargs):
591614
if "content_disposition" in observable_data
592615
else None,
593616
}
617+
if attribute is not None:
618+
input_variables["EmailMimePartType"][
619+
attribute
620+
] = simple_observable_value
594621
elif type == "Artifact":
595622
hashes = []
596623
for key, value in observable_data["hashes"].items():
@@ -611,10 +638,23 @@ def create(self, **kwargs):
611638
if "decryption_key" in observable_data
612639
else None,
613640
}
641+
if attribute is not None:
642+
input_variables["Artifact"][attribute] = simple_observable_value
614643
elif type == "StixFile":
615644
hashes = []
616-
for key, value in observable_data["hashes"].items():
617-
hashes.append({"algorithm": key, "hash": value})
645+
if simple_observable_key == "File.MD5":
646+
hashes.append({"algorithm": "MD5", "hash": simple_observable_value})
647+
if simple_observable_key == "File.SHA-1":
648+
hashes.append(
649+
{"algorithm": "SHA-1", "hash": simple_observable_value}
650+
)
651+
if simple_observable_key == "File.SHA-256":
652+
hashes.append(
653+
{"algorithm": "SHA-256", "hash": simple_observable_value}
654+
)
655+
if "hashes" in observable_data:
656+
for key, value in observable_data["hashes"].items():
657+
hashes.append({"algorithm": key, "hash": value})
618658
input_variables["StixFile"] = {
619659
"hashes": hashes if len(hashes) > 0 else None,
620660
"extensions": observable_data["extensions"]
@@ -691,30 +731,42 @@ def create(self, **kwargs):
691731
if "subject_public_key_exponent" in observable_data
692732
else None,
693733
}
734+
if attribute is not None:
735+
input_variables["X509Certificate"][
736+
attribute
737+
] = simple_observable_value
694738
elif type == "IPv4-Addr":
695739
input_variables["IPv4Addr"] = {
696740
"value": observable_data["value"]
697741
if "value" in observable_data
698742
else None,
699743
}
744+
if attribute is not None:
745+
input_variables["IPv4Addr"][attribute] = simple_observable_value
700746
elif type == "IPv6-Addr":
701747
input_variables["IPv6Addr"] = {
702748
"value": observable_data["value"]
703749
if "value" in observable_data
704750
else None,
705751
}
752+
if attribute is not None:
753+
input_variables["IPv6Addr"][attribute] = simple_observable_value
706754
elif type == "Mac-Addr":
707755
input_variables["MacAddr"] = {
708756
"value": observable_data["value"]
709757
if "value" in observable_data
710758
else None,
711759
}
760+
if attribute is not None:
761+
input_variables["MacAddr"][attribute] = simple_observable_value
712762
elif type == "Mutex":
713763
input_variables["Mutex"] = {
714764
"name": observable_data["name"]
715765
if "name" in observable_data
716766
else None,
717767
}
768+
if attribute is not None:
769+
input_variables["Mutex"][attribute] = simple_observable_value
718770
elif type == "Network-Traffic":
719771
input_variables["NetworkTraffic"] = {
720772
"extensions": observable_data["extensions"]
@@ -749,6 +801,10 @@ def create(self, **kwargs):
749801
if "dst_packets" in observable_data
750802
else None,
751803
}
804+
if attribute is not None:
805+
input_variables["NetworkTraffic"][
806+
attribute
807+
] = simple_observable_value
752808
elif type == "Process":
753809
input_variables["Process"] = {
754810
"extensions": observable_data["extensions"]
@@ -769,6 +825,8 @@ def create(self, **kwargs):
769825
if "environment_variables" in observable_data
770826
else None,
771827
}
828+
if attribute is not None:
829+
input_variables["Process"][attribute] = simple_observable_value
772830
elif type == "Software":
773831
input_variables["Software"] = {
774832
"name": observable_data["name"]
@@ -788,12 +846,16 @@ def create(self, **kwargs):
788846
if "version" in observable_data
789847
else None,
790848
}
849+
if attribute is not None:
850+
input_variables["Software"][attribute] = simple_observable_value
791851
elif type == "Url":
792852
input_variables["Url"] = {
793853
"value": observable_data["value"]
794854
if "value" in observable_data
795855
else None,
796856
}
857+
if attribute is not None:
858+
input_variables["Url"][attribute] = simple_observable_value
797859
elif type == "User-Account":
798860
input_variables["UserAccount"] = {
799861
"extensions": observable_data["extensions"]
@@ -844,6 +906,8 @@ def create(self, **kwargs):
844906
if "account_last_login" in observable_data
845907
else None,
846908
}
909+
if attribute is not None:
910+
input_variables["UserAccount"][attribute] = simple_observable_value
847911
elif type == "Windows-Registry-Key":
848912
input_variables["WindowsRegistryKey "] = {
849913
"attribute_key": observable_data["attribute_key"]
@@ -856,8 +920,12 @@ def create(self, **kwargs):
856920
if "number_of_subkeys" in observable_data
857921
else None,
858922
}
923+
if attribute is not None:
924+
input_variables["WindowsRegistryKey"][
925+
attribute
926+
] = simple_observable_value
859927
elif type == "Windows-Registry-Value-Type":
860-
input_variables["WindowsRegistryKey "] = {
928+
input_variables["WindowsRegistryKeyValueType "] = {
861929
"name": observable_data["name"]
862930
if "name" in observable_data
863931
else None,
@@ -868,6 +936,10 @@ def create(self, **kwargs):
868936
if "data_type" in observable_data
869937
else None,
870938
}
939+
if attribute is not None:
940+
input_variables["WindowsRegistryKeyValueType"][
941+
attribute
942+
] = simple_observable_value
871943
elif type == "X509-V3-Extensions-Type":
872944
input_variables["X509V3ExtensionsType "] = {
873945
"basic_constraints": observable_data["basic_constraints"]
@@ -933,36 +1005,60 @@ def create(self, **kwargs):
9331005
if "policy_mappings" in observable_data
9341006
else None,
9351007
}
1008+
if attribute is not None:
1009+
input_variables["X509V3ExtensionsType"][
1010+
attribute
1011+
] = simple_observable_value
9361012
elif type == "X-OpenCTI-Cryptographic-Key":
9371013
input_variables["XOpenCTICryptographicKey"] = {
9381014
"value": observable_data["value"]
9391015
if "value" in observable_data
9401016
else None,
9411017
}
1018+
if attribute is not None:
1019+
input_variables["XOpenCTICryptographicKey"][
1020+
attribute
1021+
] = simple_observable_value
9421022
elif type == "X-OpenCTI-Cryptocurrency-Wallet":
9431023
input_variables["XOpenCTICryptocurrencyWallet"] = {
9441024
"value": observable_data["value"]
9451025
if "value" in observable_data
9461026
else None,
9471027
}
1028+
if attribute is not None:
1029+
input_variables["XOpenCTICryptocurrencyWallet"][
1030+
attribute
1031+
] = simple_observable_value
9481032
elif type == "X-OpenCTI-Hostname":
9491033
input_variables["XOpenCTIHostname"] = {
9501034
"value": observable_data["value"]
9511035
if "value" in observable_data
9521036
else None,
9531037
}
1038+
if attribute is not None:
1039+
input_variables["XOpenCTIHostname"][
1040+
attribute
1041+
] = simple_observable_value
9541042
elif type == "X-OpenCTI-Text":
9551043
input_variables["XOpenCTIText"] = {
9561044
"value": observable_data["value"]
9571045
if "value" in observable_data
9581046
else None,
9591047
}
1048+
if attribute is not None:
1049+
input_variables["XOpenCTIUserAgent"][
1050+
attribute
1051+
] = simple_observable_value
9601052
elif type == "X-OpenCTI-User-Agent":
9611053
input_variables["XOpenCTIUserAgent"] = {
9621054
"value": observable_data["value"]
9631055
if "value" in observable_data
9641056
else None,
9651057
}
1058+
if attribute is not None:
1059+
input_variables["XOpenCTIUserAgent"][
1060+
attribute
1061+
] = simple_observable_value
9661062
result = self.opencti.query(query, input_variables)
9671063
return self.opencti.process_multiple_fields(
9681064
result["data"]["stixCyberObservableAdd"]

pycti/utils/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class StixCyberObservableTypes(Enum):
3030
X_OPENCTI_CRYPTOCURRENCY_WALLET = "X-OpenCTI-Cryptocurrency-Wallet"
3131
X_OPENCTI_TEXT = "X-OpenCTI-Text"
3232
X_OPENCTI_USER_AGENT = "X-OpenCTI-User-Agent"
33+
X_OPENCTI_SIMPLE_OBSERVABLE = "X-OpenCTI-Simple-Observable"
3334

3435
@classmethod
3536
def has_value(cls, value):

pycti/utils/opencti_stix2.py

+35-15
Original file line numberDiff line numberDiff line change
@@ -676,21 +676,41 @@ def import_observable(self, stix_object, update=False, types=None):
676676
"external_references_ids": external_references_ids,
677677
"reports": reports,
678678
}
679-
680-
stix_observable_result = self.opencti.stix_observable.create(
681-
observableData=stix_object,
682-
createdBy=extras["created_by_id"] if "created_by_id" in extras else None,
683-
objectMarking=extras["object_marking_ids"]
684-
if "object_marking_ids" in extras
685-
else [],
686-
objectLabel=extras["object_label_ids"]
687-
if "object_label_ids" in extras
688-
else [],
689-
externalReferences=extras["external_references_ids"]
690-
if "external_references_ids" in extras
691-
else [],
692-
update=update,
693-
)
679+
if stix_object["type"] == "x-opencti-simple-observable":
680+
stix_observable_result = self.opencti.stix_cyber_observable.create(
681+
simple_observable_key=stix_object["key"],
682+
simple_observable_value=stix_object["value"],
683+
createdBy=extras["created_by_id"]
684+
if "created_by_id" in extras
685+
else None,
686+
objectMarking=extras["object_marking_ids"]
687+
if "object_marking_ids" in extras
688+
else [],
689+
objectLabel=extras["object_label_ids"]
690+
if "object_label_ids" in extras
691+
else [],
692+
externalReferences=extras["external_references_ids"]
693+
if "external_references_ids" in extras
694+
else [],
695+
update=update,
696+
)
697+
else:
698+
stix_observable_result = self.opencti.stix_cyber_observable.create(
699+
observableData=stix_object,
700+
createdBy=extras["created_by_id"]
701+
if "created_by_id" in extras
702+
else None,
703+
objectMarking=extras["object_marking_ids"]
704+
if "object_marking_ids" in extras
705+
else [],
706+
objectLabel=extras["object_label_ids"]
707+
if "object_label_ids" in extras
708+
else [],
709+
externalReferences=extras["external_references_ids"]
710+
if "external_references_ids" in extras
711+
else [],
712+
update=update,
713+
)
694714
if stix_observable_result is not None:
695715
if "id" in stix_object:
696716
self.mapping_cache[stix_object["id"]] = {

0 commit comments

Comments
 (0)