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

Commit 235b86e

Browse files
authored
Add basic pytest framework (#89)
* Add basic test framework * Fix formatting * Bump dependencies and add test token * Fix Tests * Add documentation for Indicator class
1 parent f8eafca commit 235b86e

File tree

7 files changed

+249
-25
lines changed

7 files changed

+249
-25
lines changed

.circleci/config.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: 2.1
33
jobs:
44
ensure_formatting:
55
docker:
6-
- image: circleci/python:3.7.7
6+
- image: circleci/python:3.9
77
working_directory: ~/repo
88
steps:
99
- checkout
@@ -25,10 +25,24 @@ jobs:
2525
- run:
2626
name: flake8
2727
command: flake8 --ignore=E,W ~/repo
28+
tests:
29+
docker:
30+
- image: circleci/python:3.9
31+
steps:
32+
- checkout
33+
- run:
34+
name: install dependencies
35+
command: pip3 install -r requirements.txt --user
36+
- run:
37+
name: install pytest
38+
command: pip3 install pytest pytest-cov --user
39+
- run:
40+
name: run pytest
41+
command: python3 -m pytest --cov=pycti
2842
build:
2943
working_directory: ~/opencti-client
3044
docker:
31-
- image: circleci/python:3.6
45+
- image: circleci/python:3.9
3246
steps:
3347
- checkout
3448
- run:
@@ -44,7 +58,7 @@ jobs:
4458
deploy:
4559
working_directory: ~/opencti-client
4660
docker:
47-
- image: circleci/python:3.6
61+
- image: circleci/python:3.9
4862
steps:
4963
- checkout
5064
- attach_workspace:
@@ -74,6 +88,7 @@ workflows:
7488
jobs:
7589
- ensure_formatting
7690
- linter
91+
- tests
7792
opencti_client_python:
7893
jobs:
7994
- build:

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
author = "OpenCTI Project"
2424

2525
# The full version, including alpha/beta/rc tags
26-
release = "3.3.3"
26+
release = "4.2.0"
2727

2828
master_doc = "index"
2929

docs/pycti/pycti.rst

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Classes
2929
- :py:class:`OpenCTIApiConnector`:
3030
OpenCTIApiConnector
3131

32-
- :py:class:`OpenCTIApiJob`:
32+
- :py:class:`OpenCTIApiWork`:
3333
OpenCTIApiJob
3434

3535
- :py:class:`ConnectorType`:
@@ -128,9 +128,18 @@ Classes
128128
- :py:class:`OpenCTIStix2Splitter`:
129129
Undocumented.
130130

131+
- :py:class:`OpenCTIStix2Update`:
132+
Python API for Stix2 Update in OpenCTI
133+
134+
- :py:class:`OpenCTIStix2Utils`:
135+
Undocumented.
136+
131137
- :py:class:`StixCyberObservableTypes`:
132138
An enumeration.
133139

140+
- :py:class:`SimpleObservable`:
141+
Base class for STIX object types
142+
134143

135144
.. autoclass:: OpenCTIApiClient
136145
:members:
@@ -146,11 +155,11 @@ Classes
146155
.. inheritance-diagram:: OpenCTIApiConnector
147156
:parts: 1
148157

149-
.. autoclass:: OpenCTIApiJob
158+
.. autoclass:: OpenCTIApiWork
150159
:members:
151160

152161
.. rubric:: Inheritance
153-
.. inheritance-diagram:: OpenCTIApiJob
162+
.. inheritance-diagram:: OpenCTIApiWork
154163
:parts: 1
155164

156165
.. autoclass:: ConnectorType
@@ -377,9 +386,30 @@ Classes
377386
.. inheritance-diagram:: OpenCTIStix2Splitter
378387
:parts: 1
379388

389+
.. autoclass:: OpenCTIStix2Update
390+
:members:
391+
392+
.. rubric:: Inheritance
393+
.. inheritance-diagram:: OpenCTIStix2Update
394+
:parts: 1
395+
396+
.. autoclass:: OpenCTIStix2Utils
397+
:members:
398+
399+
.. rubric:: Inheritance
400+
.. inheritance-diagram:: OpenCTIStix2Utils
401+
:parts: 1
402+
380403
.. autoclass:: StixCyberObservableTypes
381404
:members:
382405

383406
.. rubric:: Inheritance
384407
.. inheritance-diagram:: StixCyberObservableTypes
385408
:parts: 1
409+
410+
.. autoclass:: SimpleObservable
411+
:members:
412+
413+
.. rubric:: Inheritance
414+
.. inheritance-diagram:: SimpleObservable
415+
:parts: 1

pycti/entities/opencti_indicator.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self, opencti):
4141
color
4242
}
4343
}
44-
}
44+
}
4545
}
4646
... on Organization {
4747
x_opencti_organization_type
@@ -112,7 +112,7 @@ def __init__(self, opencti):
112112
edges {
113113
node {
114114
id
115-
standard_id
115+
standard_id
116116
entity_type
117117
kill_chain_name
118118
phase_name
@@ -139,6 +139,9 @@ def list(self, **kwargs):
139139
:param list customAttributes: (optional) list of attributes keys to return
140140
:param bool getAll: (optional) switch to return all entries (be careful to use this without any other filters)
141141
:param bool withPagination: (optional) switch to use pagination
142+
143+
:return: List of Indicators
144+
:rtype: list
142145
"""
143146

144147
filters = kwargs.get("filters", None)
@@ -228,6 +231,9 @@ def read(self, **kwargs):
228231
229232
:param str id: the id of the Threat-Actor
230233
:param list filters: the filters to apply if no id provided
234+
235+
:return: Indicator object
236+
:rtype: Indicator
231237
"""
232238

233239
id = kwargs.get("id", None)
@@ -264,14 +270,17 @@ def read(self, **kwargs):
264270
)
265271
return None
266272

267-
"""
268-
Create a Indicator object
273+
def create(self, **kwargs):
274+
"""
275+
Create an Indicator object
269276
270-
:param name: the name of the Indicator
271-
:return Indicator object
272-
"""
277+
:param str name: the name of the Indicator
278+
:param str pattern: stix indicator pattern
279+
:param str x_opencti_main_observable_type: type of the observable
273280
274-
def create(self, **kwargs):
281+
:return: Indicator object
282+
:rtype: Indicator
283+
"""
275284
stix_id = kwargs.get("stix_id", None)
276285
created_by = kwargs.get("createdBy", None)
277286
object_marking = kwargs.get("objectMarking", None)
@@ -364,15 +373,16 @@ def create(self, **kwargs):
364373
"[opencti_indicator] Missing parameters: name or pattern or x_opencti_main_observable_type",
365374
)
366375

367-
"""
376+
def add_stix_observable(self, **kwargs):
377+
"""
368378
Add a Stix-Observable object to Indicator object (based-on)
369379
370380
:param id: the id of the Indicator
371-
:param entity_id: the id of the Stix-Observable
372-
:return Boolean
373-
"""
381+
:param indicator: Indicator object
382+
:param stix_cyber_observable_id: the id of the Stix-Observable
374383
375-
def add_stix_observable(self, **kwargs):
384+
:return: Boolean True if there has been no import error
385+
"""
376386
id = kwargs.get("id", None)
377387
indicator = kwargs.get("indicator", None)
378388
stix_cyber_observable_id = kwargs.get("stix_cyber_observable_id", None)
@@ -423,14 +433,17 @@ def add_stix_observable(self, **kwargs):
423433
)
424434
return False
425435

426-
"""
436+
def import_from_stix2(self, **kwargs):
437+
"""
427438
Import an Indicator object from a STIX2 object
428439
429440
:param stixObject: the Stix-Object Indicator
430-
:return Indicator object
431-
"""
441+
:param extras: extra dict
442+
:param bool update: set the update flag on import
432443
433-
def import_from_stix2(self, **kwargs):
444+
:return: Indicator object
445+
:rtype: Indicator
446+
"""
434447
stix_object = kwargs.get("stixObject", None)
435448
extras = kwargs.get("extras", {})
436449
update = kwargs.get("update", False)

tests/01-unit/test_stix_split.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ def test_split_bundle():
77
with open("./tests/data/enterprise-attack.json") as file:
88
content = file.read()
99
bundles = stix_splitter.split_bundle(content)
10-
assert len(bundles) == 7028
10+
assert len(bundles) == 7029

tests/data/indicator_stix.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"type": "bundle",
3+
"id": "bundle--3e0921e7-0d87-49c4-8121-ec28db0f535f",
4+
"objects": [
5+
{
6+
"id": "indicator--39915430-6d24-4f9d-a871-76a264169674",
7+
"type": "indicator",
8+
"spec_version": "2.1",
9+
"name": "103.83.192.6",
10+
"labels": [
11+
"indicator"
12+
],
13+
"pattern": "[ipv4-addr:value = '103.83.192.6']",
14+
"valid_from": "2020-04-17T09:50:27.000Z",
15+
"valid_until": "2021-04-17T09:50:27.000Z",
16+
"x_opencti_pattern_type": "stix",
17+
"created": "2020-04-30T06:29:41.232Z",
18+
"modified": "2020-04-30T06:29:41.232Z",
19+
"x_opencti_score": 50,
20+
"x_opencti_id": "40caf0b8-0632-4f62-9ba6-dc10c093a63e",
21+
"created_by_ref": "identity--26926631-96e9-40e9-9ac0-ab93384f0ce7",
22+
"object_marking_refs": [
23+
"marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
24+
]
25+
},
26+
{
27+
"id": "indicator--41be0c61-43b3-4233-aea0-7077a8981c77",
28+
"type": "indicator",
29+
"spec_version": "2.1",
30+
"name": "192.185.46.253",
31+
"labels": [
32+
"indicator"
33+
],
34+
"pattern": "[ipv4-addr:value = '192.185.46.253']",
35+
"valid_from": "2020-04-17T09:50:27.000Z",
36+
"valid_until": "2021-04-17T09:50:27.000Z",
37+
"x_opencti_pattern_type": "stix",
38+
"created": "2020-04-30T06:29:41.303Z",
39+
"modified": "2020-04-30T06:29:41.303Z",
40+
"x_opencti_score": 50,
41+
"x_opencti_id": "2962d903-76ad-4092-b4e0-2021a30db0cb",
42+
"created_by_ref": "identity--26926631-96e9-40e9-9ac0-ab93384f0ce7",
43+
"object_marking_refs": [
44+
"marking-definition--613f2e26-407d-48c7-9eca-b8e91df99dc9"
45+
]
46+
}
47+
]
48+
}

0 commit comments

Comments
 (0)