Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit 91bc71d

Browse files
author
Samuel Hassine
committed
[client] Fix some bugs and add the tags management
1 parent 6e61fe4 commit 91bc71d

File tree

6 files changed

+173
-7
lines changed

6 files changed

+173
-7
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,18 @@
1717
)
1818

1919
# Create the tag (if not exists)
20+
tag = opencti_api_client.tag.create(
21+
tag_type='Malware-Type',
22+
value='Ranswomware',
23+
color='#ffa500',
24+
)
2025

2126
# Add the tag
27+
opencti_api_client.stix_entity.add_tag(
28+
id=malware['id'],
29+
tag_id=tag['id']
30+
)
2231

2332
# Print
33+
malware = opencti_api_client.malware.read(id=malware['id'])
2434
print(malware)

examples/create_incident_with_ttps_and_indicators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pycti import OpenCTIApiClient
77

88
# Variables
9-
api_url = 'https://demo.opencti.io'
9+
api_url = 'http://localhost:4000'
1010
api_token = 'fa63eb1f-bf14-4777-9190-43b4571cbc8b'
1111

1212
# OpenCTI initialization
@@ -166,4 +166,4 @@
166166
for object_ref in object_refs:
167167
opencti_api_client.report.add_stix_entity(id=report['id'], report=report, entity_id=object_ref)
168168
for observable_ref in observable_refs:
169-
opencti_api_client.report.add_stix_observable(id=report['id'], report=report, entity_id=observable_ref)
169+
opencti_api_client.report.add_stix_observable(id=report['id'], report=report, stix_observable_id=observable_ref)

pycti/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pycti.connector.opencti_connector import OpenCTIConnector
77
from pycti.connector.opencti_connector_helper import OpenCTIConnectorHelper, get_config_variable
88

9+
from pycti.entities.opencti_tag import Tag
910
from pycti.entities.opencti_marking_definition import MarkingDefinition
1011
from pycti.entities.opencti_external_reference import ExternalReference
1112
from pycti.entities.opencti_kill_chain_phase import KillChainPhase

pycti/api/opencti_api_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from pycti.utils.constants import ObservableTypes
1717
from pycti.utils.opencti_stix2 import OpenCTIStix2
1818

19+
from pycti.entities.opencti_tag import Tag
1920
from pycti.entities.opencti_marking_definition import MarkingDefinition
2021
from pycti.entities.opencti_external_reference import ExternalReference
2122
from pycti.entities.opencti_kill_chain_phase import KillChainPhase
@@ -78,6 +79,7 @@ def __init__(self, url, token, log_level='info', ssl_verify=False):
7879
self.stix2 = OpenCTIStix2(self)
7980

8081
# Define the entities
82+
self.tag = Tag(self)
8183
self.marking_definition = MarkingDefinition(self)
8284
self.external_reference = ExternalReference(self)
8385
self.kill_chain_phase = KillChainPhase(self)

pycti/entities/opencti_tag.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# coding: utf-8
2+
3+
import json
4+
5+
6+
class Tag:
7+
def __init__(self, opencti):
8+
self.opencti = opencti
9+
self.properties = """
10+
id
11+
tag_type
12+
value
13+
color
14+
created_at
15+
updated_at
16+
"""
17+
18+
"""
19+
List Tag objects
20+
21+
:param filters: the filters to apply
22+
:param first: return the first n rows from the after ID (or the beginning if not set)
23+
:param after: ID of the first row for pagination
24+
:return List of Tag objects
25+
"""
26+
27+
def list(self, **kwargs):
28+
filters = kwargs.get('filters', None)
29+
first = kwargs.get('first', 500)
30+
after = kwargs.get('after', None)
31+
order_by = kwargs.get('orderBy', None)
32+
order_mode = kwargs.get('orderMode', None)
33+
self.opencti.log('info', 'Listing Tags with filters ' + json.dumps(filters) + '.')
34+
query = """
35+
query Tags($filters: [TagsFiltering], $first: Int, $after: ID, $orderBy: TagsOrdering, $orderMode: OrderingMode) {
36+
tags(filters: $filters, first: $first, after: $after, orderBy: $orderBy, orderMode: $orderMode) {
37+
edges {
38+
node {
39+
""" + self.properties + """
40+
}
41+
}
42+
pageInfo {
43+
startCursor
44+
endCursor
45+
hasNextPage
46+
hasPreviousPage
47+
globalCount
48+
}
49+
}
50+
}
51+
"""
52+
result = self.opencti.query(query, {
53+
'filters': filters,
54+
'first': first,
55+
'after': after,
56+
'orderBy': order_by,
57+
'orderMode': order_mode
58+
})
59+
return self.opencti.process_multiple(result['data']['tags'])
60+
61+
"""
62+
Read a Tag object
63+
64+
:param id: the id of the Tag
65+
:param filters: the filters to apply if no id provided
66+
:return Marking-Definition object
67+
"""
68+
69+
def read(self, **kwargs):
70+
id = kwargs.get('id', None)
71+
filters = kwargs.get('filters', None)
72+
if id is not None:
73+
self.opencti.log('info', 'Reading Tag {' + id + '}.')
74+
query = """
75+
query Tag($id: String!) {
76+
tag(id: $id) {
77+
""" + self.properties + """
78+
}
79+
}
80+
"""
81+
result = self.opencti.query(query, {'id': id})
82+
return self.opencti.process_multiple_fields(result['data']['tag'])
83+
elif filters is not None:
84+
result = self.list(filters=filters)
85+
if len(result) > 0:
86+
return result[0]
87+
else:
88+
return None
89+
else:
90+
self.opencti.log('error', 'Missing parameters: id or filters')
91+
return None
92+
93+
"""
94+
Create a Tag object
95+
96+
:param tag_type: the tag type
97+
:param value: the value
98+
:param color: the color
99+
:return Tag object
100+
"""
101+
102+
def create_raw(self, **kwargs):
103+
tag_type = kwargs.get('tag_type', None)
104+
value = kwargs.get('value', None)
105+
color = kwargs.get('color', None)
106+
107+
if tag_type is not None and value is not None and color is not None:
108+
query = """
109+
mutation TagAdd($input: TagAddInput) {
110+
tagAdd(input: $input) {
111+
""" + self.properties + """
112+
}
113+
}
114+
"""
115+
result = self.opencti.query(query, {
116+
'input': {
117+
'tag_type': tag_type,
118+
'value': value,
119+
'color': color
120+
}
121+
})
122+
return self.opencti.process_multiple_fields(result['data']['tagAdd'])
123+
else:
124+
self.opencti.log('error', '[opencti_tag] Missing parameters: tag_type and value and color')
125+
126+
"""
127+
Create a Tag object only if it not exists, update it on request
128+
129+
:param tag_type: the tag type
130+
:param value: the value
131+
:param color: the color
132+
:return Tag object
133+
"""
134+
135+
def create(self, **kwargs):
136+
tag_type = kwargs.get('tag_type', None)
137+
value = kwargs.get('value', None)
138+
color = kwargs.get('color', None)
139+
140+
object_result = self.read(filters=[
141+
{'key': 'tag_type', 'values': [tag_type]},
142+
{'key': 'value', 'values': [value]}
143+
])
144+
if object_result is not None:
145+
return object_result
146+
else:
147+
return self.create_raw(
148+
tag_type=tag_type,
149+
value=value,
150+
color=color,
151+
)

pycti/utils/opencti_stix2.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def extract_embedded_relationships(self, stix_object, types=None):
254254
published=published,
255255
report_class='Threat Report',
256256
object_status=2,
257-
createdByRef=author,
257+
createdByRef=author['id'] if author is not None else None,
258258
update=True
259259
)
260260
# Add marking
@@ -1423,8 +1423,10 @@ def import_bundle(self, stix_bundle, update=False, types=None) -> List:
14231423
for item in stix_bundle['objects']:
14241424
if item['type'] == 'relationship':
14251425
# Import only relationships between entities
1426-
if 'relationship' not in item[CustomProperties.SOURCE_REF] and \
1427-
'relationship' not in item[CustomProperties.TARGET_REF]:
1426+
if (CustomProperties.SOURCE_REF not in item or 'relationship' not in item[
1427+
CustomProperties.SOURCE_REF]) and (
1428+
CustomProperties.TARGET_REF not in item or 'relationship' not in item[
1429+
CustomProperties.TARGET_REF]):
14281430
self.import_relationship(item, update, types)
14291431
imported_elements.append({'id': item['id'], 'type': item['type']})
14301432
end_time = time.time()
@@ -1434,8 +1436,8 @@ def import_bundle(self, stix_bundle, update=False, types=None) -> List:
14341436
start_time = time.time()
14351437
for item in stix_bundle['objects']:
14361438
if item['type'] == 'relationship':
1437-
if 'relationship' in item[CustomProperties.SOURCE_REF] or \
1438-
'relationship' in item[CustomProperties.TARGET_REF]:
1439+
if (CustomProperties.SOURCE_REF in item and 'relationship' in item[CustomProperties.SOURCE_REF]) or (
1440+
CustomProperties.TARGET_REF in item and 'relationship' in item[CustomProperties.TARGET_REF]):
14391441
self.import_relationship(item, update, types)
14401442
imported_elements.append({'id': item['id'], 'type': item['type']})
14411443
end_time = time.time()

0 commit comments

Comments
 (0)