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

Commit 8e65c2d

Browse files
[client] Split threat actors into 2 sub types (#420)
Co-authored-by: sarahBocognano <[email protected]>
1 parent cd13554 commit 8e65c2d

13 files changed

+558
-79
lines changed

.drone.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ steps:
66
- name: sleep-for-opencti
77
image: python:3.11
88
commands:
9-
- sleep 100
9+
- sleep 180
1010
- name: client-test-38
1111
image: python:3.8
1212
commands:
@@ -87,7 +87,7 @@ services:
8787
EXPIRATION_SCHEDULER__ENABLED: false
8888
SUBSCRIPTION_SCHEDULER__ENABLED: false
8989
commands:
90-
- apk add build-base git libffi-dev
90+
- apk add build-base git libffi-dev cargo
9191
- BRANCH=$(echo $DRONE_COMMIT_BRANCH)
9292
- OPENCTI_BRANCH=$([ $(echo $BRANCH | cut -d "/" -f 1) == opencti ] && echo issue/$(echo $BRANCH | cut -d "/" -f 2) || echo 'master' )
9393
- git clone -b $OPENCTI_BRANCH https://github.com/OpenCTI-Platform/opencti.git /tmp/opencti

pycti/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from .entities.opencti_task import Task
4747
from .entities.opencti_threat_actor import ThreatActor
4848
from .entities.opencti_threat_actor_group import ThreatActorGroup
49+
from .entities.opencti_threat_actor_individual import ThreatActorIndividual
4950
from .entities.opencti_tool import Tool
5051
from .entities.opencti_vulnerability import Vulnerability
5152
from .utils.constants import (
@@ -113,6 +114,7 @@
113114
"StixSightingRelationship",
114115
"ThreatActor",
115116
"ThreatActorGroup",
117+
"ThreatActorIndividual",
116118
"Tool",
117119
"Vulnerability",
118120
"get_config_variable",

pycti/api/opencti_api_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from pycti.entities.opencti_task import Task
6161
from pycti.entities.opencti_threat_actor import ThreatActor
6262
from pycti.entities.opencti_threat_actor_group import ThreatActorGroup
63+
from pycti.entities.opencti_threat_actor_individual import ThreatActorIndividual
6364
from pycti.entities.opencti_tool import Tool
6465
from pycti.entities.opencti_vocabulary import Vocabulary
6566
from pycti.entities.opencti_vulnerability import Vulnerability
@@ -183,6 +184,7 @@ def __init__(
183184
self.location = Location(self)
184185
self.threat_actor = ThreatActor(self)
185186
self.threat_actor_group = ThreatActorGroup(self)
187+
self.threat_actor_individual = ThreatActorIndividual(self)
186188
self.intrusion_set = IntrusionSet(self)
187189
self.infrastructure = Infrastructure(self)
188190
self.campaign = Campaign(self)

pycti/entities/opencti_indicator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def read(self, **kwargs):
274274
275275
Note: either `id` or `filters` is required.
276276
277-
:param str id: the id of the Threat-Actor
277+
:param str id: the id of the Threat-Actor-Group
278278
:param list filters: the filters to apply if no id provided
279279
280280
:return: Indicator object

pycti/entities/opencti_infrastructure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def read(self, **kwargs):
257257
258258
Note: either `id` or `filters` is required.
259259
260-
:param str id: the id of the Threat-Actor
260+
:param str id: the id of the Threat-Actor-Group
261261
:param list filters: the filters to apply if no id provided
262262
"""
263263

pycti/entities/opencti_stix_core_object.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,9 +349,7 @@ def __init__(self, opencti, file):
349349
resource_level
350350
primary_motivation
351351
secondary_motivations
352-
... on ThreatActorGroup {
353-
personal_motivations
354-
}
352+
personal_motivations
355353
}
356354
... on Tool {
357355
name

pycti/entities/opencti_stix_domain_object.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,7 @@ def __init__(self, opencti, file):
437437
resource_level
438438
primary_motivation
439439
secondary_motivations
440-
... on ThreatActorGroup {
441-
personal_motivations
442-
}
440+
personal_motivations
443441
}
444442
... on Tool {
445443
name

pycti/entities/opencti_stix_object_or_stix_relationship.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,7 @@ def __init__(self, opencti):
288288
resource_level
289289
primary_motivation
290290
secondary_motivations
291-
... on ThreatActorGroup {
292-
personal_motivations
293-
}
291+
personal_motivations
294292
}
295293
... on Tool {
296294
name

pycti/entities/opencti_threat_actor.py

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pycti.entities import LOGGER
1010
from pycti.entities.opencti_threat_actor_group import ThreatActorGroup
11+
from pycti.entities.opencti_threat_actor_individual import ThreatActorIndividual
1112

1213

1314
class ThreatActor:
@@ -21,6 +22,7 @@ def __init__(self, opencti):
2122

2223
self.opencti = opencti
2324
self.threat_actor_group = ThreatActorGroup(opencti)
25+
self.threat_actor_individual = ThreatActorIndividual(opencti)
2426
self.properties = """
2527
id
2628
standard_id
@@ -186,11 +188,11 @@ def list(self, **kwargs) -> dict:
186188
LOGGER.info("Listing Threat-Actors with filters %s.", json.dumps(filters))
187189
query = (
188190
"""
189-
query ThreatActors($filters: [ThreatActorsFiltering], $search: String, $first: Int, $after: ID, $orderBy: ThreatActorsOrdering, $orderMode: OrderingMode) {
190-
threatActors(filters: $filters, search: $search, first: $first, after: $after, orderBy: $orderBy, orderMode: $orderMode) {
191-
edges {
192-
node {
193-
"""
191+
query ThreatActors($filters: [ThreatActorsFiltering], $search: String, $first: Int, $after: ID, $orderBy: ThreatActorsOrdering, $orderMode: OrderingMode) {
192+
threatActors(filters: $filters, search: $search, first: $first, after: $after, orderBy: $orderBy, orderMode: $orderMode) {
193+
edges {
194+
node {
195+
"""
194196
+ (custom_attributes if custom_attributes is not None else self.properties)
195197
+ """
196198
}
@@ -242,9 +244,9 @@ def read(self, **kwargs) -> Union[dict, None]:
242244
LOGGER.info("Reading Threat-Actor {%s}.", id)
243245
query = (
244246
"""
245-
query ThreatActor($id: String!) {
246-
threatActor(id: $id) {
247-
"""
247+
query ThreatActor($id: String!) {
248+
threatActor(id: $id) {
249+
"""
248250
+ (
249251
custom_attributes
250252
if custom_attributes is not None
@@ -267,41 +269,9 @@ def read(self, **kwargs) -> Union[dict, None]:
267269
LOGGER.error("[opencti_threat_actor] Missing parameters: id or filters")
268270
return None
269271

272+
@DeprecationWarning
270273
def create(self, **kwargs):
271-
"""Create a Threat-Actor object
272-
273-
The Threat-Actor entity will only be created if it doesn't exists
274-
By setting `update` to `True` it acts like an upsert and updates
275-
fields of an existing Threat-Actor entity.
276-
277-
The create method accepts the following kwargs.
278-
279-
Note: `name` and `description` or `stix_id` is required.
280-
281-
:param str stix_id: stix2 id reference for the Threat-Actor entity
282-
:param str createdBy: (optional) id of the organization that created the knowledge
283-
:param list objectMarking: (optional) list of OpenCTI markin definition ids
284-
:param list objectLabel: (optional) list of OpenCTI label ids
285-
:param list externalReferences: (optional) list of OpenCTI external references ids
286-
:param bool revoked: is this entity revoked
287-
:param int confidence: confidence level
288-
:param str lang: language
289-
:param str created: (optional) date in OpenCTI date format
290-
:param str modified: (optional) date in OpenCTI date format
291-
:param str name: name of the threat actor
292-
:param str description: description of the threat actor
293-
:param list aliases: (optional) list of alias names for the Threat-Actor
294-
:param list threat_actor_types: (optional) list of threat actor types
295-
:param str first_seen: (optional) date in OpenCTI date format
296-
:param str last_seen: (optional) date in OpenCTI date format
297-
:param list roles: (optional) list of roles
298-
:param list goals: (optional) list of goals
299-
:param str sophistication: (optional) describe the actors sophistication in text
300-
:param str resource_level: (optional) describe the actors resource_level in text
301-
:param str primary_motivation: (optional) describe the actors primary_motivation in text
302-
:param list secondary_motivations: (optional) describe the actors secondary_motivations in list of string
303-
:param bool update: (optional) choose to updated an existing Threat-Actor entity, default `False`
304-
"""
274+
# For backward compatibility, please use threat_actor_group or threat_actor_individual
305275
return self.threat_actor_group.create(**kwargs)
306276

307277
"""
@@ -312,4 +282,17 @@ def create(self, **kwargs):
312282
"""
313283

314284
def import_from_stix2(self, **kwargs):
315-
return self.threat_actor_group.import_from_stix2(**kwargs)
285+
stix_object = kwargs.get("stixObject", None)
286+
if "x_opencti_type" in stix_object:
287+
type = stix_object["x_opencti_type"].lower()
288+
elif self.opencti.get_attribute_in_extension("type", stix_object) is not None:
289+
type = self.opencti.get_attribute_in_extension("type", stix_object).lower()
290+
elif "individual" in stix_object["resource_level"].lower():
291+
type = "threat-actor-individual"
292+
else:
293+
type = "threat-actor-group"
294+
295+
if "threat-actor-group" in type:
296+
return self.threat_actor_group.import_from_stix2(**kwargs)
297+
if "threat-actor-individual" in type:
298+
return self.threat_actor_individual.import_from_stix2(**kwargs)

pycti/entities/opencti_threat_actor_group.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def __init__(self, opencti):
130130
resource_level
131131
primary_motivation
132132
secondary_motivations
133+
personal_motivations
133134
importFiles {
134135
edges {
135136
node {
@@ -302,6 +303,7 @@ def create(self, **kwargs):
302303
:param str resource_level: (optional) describe the actors resource_level in text
303304
:param str primary_motivation: (optional) describe the actors primary_motivation in text
304305
:param list secondary_motivations: (optional) describe the actors secondary_motivations in list of string
306+
:param list personal_motivations: (optional) describe the actors personal_motivations in list of strings
305307
:param bool update: (optional) choose to updated an existing Threat-Actor-Group entity, default `False`
306308
"""
307309

@@ -326,6 +328,7 @@ def create(self, **kwargs):
326328
resource_level = kwargs.get("resource_level", None)
327329
primary_motivation = kwargs.get("primary_motivation", None)
328330
secondary_motivations = kwargs.get("secondary_motivations", None)
331+
personal_motivations = kwargs.get("personal_motivations", None)
329332
x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
330333
granted_refs = kwargs.get("objectOrganization", None)
331334
update = kwargs.get("update", False)
@@ -368,6 +371,7 @@ def create(self, **kwargs):
368371
"resource_level": resource_level,
369372
"primary_motivation": primary_motivation,
370373
"secondary_motivations": secondary_motivations,
374+
"personal_motivations": personal_motivations,
371375
"x_opencti_stix_ids": x_opencti_stix_ids,
372376
"update": update,
373377
}
@@ -453,6 +457,9 @@ def import_from_stix2(self, **kwargs):
453457
secondary_motivations=stix_object["secondary_motivations"]
454458
if "secondary_motivations" in stix_object
455459
else None,
460+
personal_motivations=stix_object["personal_motivations"]
461+
if "personal_motivations" in stix_object
462+
else None,
456463
x_opencti_stix_ids=stix_object["x_opencti_stix_ids"]
457464
if "x_opencti_stix_ids" in stix_object
458465
else None,

0 commit comments

Comments
 (0)