pymisp 2.5.3__py3-none-any.whl → 2.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pymisp might be problematic. Click here for more details.

Files changed (162) hide show
  1. CHANGELOG.txt +5380 -0
  2. examples/__init__.py +0 -0
  3. examples/add_attributes_from_csv.py +74 -0
  4. examples/add_email_object.py +29 -0
  5. examples/add_fail2ban_object.py +86 -0
  6. examples/add_feed.py +25 -0
  7. examples/add_file_object.py +47 -0
  8. examples/add_filetype_object_from_csv.py +53 -0
  9. examples/add_generic_object.py +26 -0
  10. examples/add_github_user.py +65 -0
  11. examples/add_gitlab_user.py +56 -0
  12. examples/add_named_attribute.py +25 -0
  13. examples/add_organisations.py +57 -0
  14. examples/add_ssh_authorized_keys.py +29 -0
  15. examples/add_user.py +22 -0
  16. examples/add_vehicle_object.py +22 -0
  17. examples/addtag2.py +45 -0
  18. examples/asciidoc_generator.py +114 -0
  19. examples/cache_all.py +10 -0
  20. examples/copyTagsFromAttributesToEvent.py +68 -0
  21. examples/copy_list.py +93 -0
  22. examples/create_events.py +26 -0
  23. examples/cytomic_orion.py +549 -0
  24. examples/del.py +22 -0
  25. examples/delete_user.py +16 -0
  26. examples/edit_organisation.py +20 -0
  27. examples/edit_user.py +20 -0
  28. examples/falsepositive_disabletoids.py +136 -0
  29. examples/fetch_events_feed.py +15 -0
  30. examples/fetch_warninglist_hits.py +38 -0
  31. examples/freetext.py +22 -0
  32. examples/generate_file_objects.py +78 -0
  33. examples/generate_meta_feed.py +15 -0
  34. examples/get.py +37 -0
  35. examples/get_csv.py +37 -0
  36. examples/get_network_activity.py +187 -0
  37. examples/last.py +48 -0
  38. examples/load_csv.py +94 -0
  39. examples/lookup.py +28 -0
  40. examples/misp2cef.py +71 -0
  41. examples/misp2clamav.py +52 -0
  42. examples/openioc_to_misp.py +27 -0
  43. examples/proofpoint_tap.py +203 -0
  44. examples/proofpoint_vap.py +65 -0
  45. examples/search.py +48 -0
  46. examples/search_attributes_yara.py +40 -0
  47. examples/search_sighting.py +42 -0
  48. examples/server_sync_check_conn.py +32 -0
  49. examples/sharing_groups.py +15 -0
  50. examples/show_sightings.py +168 -0
  51. examples/stats_report.py +405 -0
  52. examples/sync_sighting.py +171 -0
  53. examples/tags.py +25 -0
  54. examples/test_sign.py +19 -0
  55. examples/trustar_misp.py +59 -0
  56. examples/up.py +21 -0
  57. examples/upload.py +60 -0
  58. examples/users_list.py +15 -0
  59. examples/vmray_automation.py +281 -0
  60. examples/vt_to_misp.py +182 -0
  61. examples/warninglists.py +22 -0
  62. examples/yara.py +38 -0
  63. examples/yara_dump.py +98 -0
  64. pymisp/api.py +33 -5
  65. pymisp/data/misp-objects/objects/android-app/definition.json +8 -2
  66. pymisp/data/misp-objects/objects/instagram-account/definition.json +66 -0
  67. pymisp/data/misp-objects/objects/lnk/definition.json +13 -1
  68. pymisp/data/misp-objects/objects/rmm/definition.json +88 -0
  69. pymisp/data/misp-objects/objects/spambee-report/definition.json +54 -0
  70. pymisp/data/misp-objects/objects/target-system/definition.json +2 -2
  71. pymisp/data/misp-objects/objects/vulnerability/definition.json +5 -4
  72. pymisp/data/misp-objects/relationships/definition.json +17 -1
  73. pymisp/data/misp-objects/schema_objects.json +1 -1
  74. pymisp/mispevent.py +95 -23
  75. {pymisp-2.5.3.dist-info → pymisp-2.5.7.dist-info}/METADATA +23 -28
  76. {pymisp-2.5.3.dist-info → pymisp-2.5.7.dist-info}/RECORD +144 -30
  77. {pymisp-2.5.3.dist-info → pymisp-2.5.7.dist-info}/WHEEL +1 -1
  78. tests/57c4445b-c548-4654-af0b-4be3950d210f.json +1 -0
  79. tests/__init__.py +0 -0
  80. tests/csv_testfiles/invalid_fieldnames.csv +11 -0
  81. tests/csv_testfiles/valid_fieldnames.csv +4 -0
  82. tests/email_testfiles/mail_1.eml.zip +0 -0
  83. tests/email_testfiles/mail_1.msg +0 -0
  84. tests/email_testfiles/mail_1_bom.eml +858 -0
  85. tests/email_testfiles/mail_1_headers_only.eml +28 -0
  86. tests/email_testfiles/mail_2.eml +32 -0
  87. tests/email_testfiles/mail_3.eml +170 -0
  88. tests/email_testfiles/mail_3.msg +0 -0
  89. tests/email_testfiles/mail_4.msg +0 -0
  90. tests/email_testfiles/mail_5.msg +0 -0
  91. tests/email_testfiles/mail_multiple_to.eml +15 -0
  92. tests/email_testfiles/source +1 -0
  93. tests/git-vuln-finder-quagga.json +1493 -0
  94. tests/misp_event.json +76 -0
  95. tests/mispevent_testfiles/attribute.json +21 -0
  96. tests/mispevent_testfiles/attribute_del.json +23 -0
  97. tests/mispevent_testfiles/def_param.json +53 -0
  98. tests/mispevent_testfiles/event.json +8 -0
  99. tests/mispevent_testfiles/event_obj_attr_tag.json +57 -0
  100. tests/mispevent_testfiles/event_obj_def_param.json +62 -0
  101. tests/mispevent_testfiles/event_obj_tag.json +29 -0
  102. tests/mispevent_testfiles/event_tags.json +18 -0
  103. tests/mispevent_testfiles/existing_event.json +4599 -0
  104. tests/mispevent_testfiles/existing_event_edited.json +4601 -0
  105. tests/mispevent_testfiles/galaxy.json +25 -0
  106. tests/mispevent_testfiles/malware.json +19 -0
  107. tests/mispevent_testfiles/malware_exist.json +163 -0
  108. tests/mispevent_testfiles/misp_custom_obj.json +38 -0
  109. tests/mispevent_testfiles/overwrite_file/definition.json +457 -0
  110. tests/mispevent_testfiles/proposals.json +35 -0
  111. tests/mispevent_testfiles/shadow.json +148 -0
  112. tests/mispevent_testfiles/sighting.json +5 -0
  113. tests/mispevent_testfiles/simple.json +2 -0
  114. tests/mispevent_testfiles/test_object_template/definition.json +29 -0
  115. tests/new_misp_event.json +34 -0
  116. tests/reportlab_testfiles/HTML_event.json +1 -0
  117. tests/reportlab_testfiles/galaxy_1.json +1250 -0
  118. tests/reportlab_testfiles/image_event.json +2490 -0
  119. tests/reportlab_testfiles/japanese_test.json +156 -0
  120. tests/reportlab_testfiles/japanese_test_heavy.json +318 -0
  121. tests/reportlab_testfiles/long_event.json +3730 -0
  122. tests/reportlab_testfiles/mainly_objects_1.json +1092 -0
  123. tests/reportlab_testfiles/mainly_objects_2.json +977 -0
  124. tests/reportlab_testfiles/sighting_1.json +305 -0
  125. tests/reportlab_testfiles/sighting_2.json +221 -0
  126. tests/reportlab_testfiles/to_delete1.json +804 -0
  127. tests/reportlab_testfiles/to_delete2.json +1 -0
  128. tests/reportlab_testfiles/to_delete3.json +1 -0
  129. tests/reportlab_testfiles/very_long_event.json +1006 -0
  130. tests/reportlab_testoutputs/to_delete1.json.pdf +391 -0
  131. tests/reportlab_testoutputs/to_delete2.json.pdf +506 -0
  132. tests/reportlab_testoutputs/to_delete3.json.pdf +277 -0
  133. tests/search_index_result.json +69 -0
  134. tests/sharing_groups.json +98 -0
  135. tests/stix1.xml-utf8 +110 -0
  136. tests/stix2.json +1 -0
  137. tests/test_analyst_data.py +123 -0
  138. tests/test_emailobject.py +157 -0
  139. tests/test_fileobject.py +20 -0
  140. tests/test_mispevent.py +473 -0
  141. tests/test_reportlab.py +431 -0
  142. tests/testlive_comprehensive.py +3734 -0
  143. tests/testlive_sync.py +474 -0
  144. pymisp/data/misp-objects/.git +0 -1
  145. pymisp/data/misp-objects/.gitchangelog.rc +0 -289
  146. pymisp/data/misp-objects/.github/workflows/codeql.yml +0 -41
  147. pymisp/data/misp-objects/.github/workflows/nosetests.yml +0 -39
  148. pymisp/data/misp-objects/.travis.yml +0 -16
  149. pymisp/data/misp-objects/LICENSE-software-only.md +0 -661
  150. pymisp/data/misp-objects/LICENSE.md +0 -36
  151. pymisp/data/misp-objects/README.md +0 -566
  152. pymisp/data/misp-objects/docs/time-related-objects.ods +0 -0
  153. pymisp/data/misp-objects/docs/time-related-objects.pdf +0 -0
  154. pymisp/data/misp-objects/jq_all_the_things.sh +0 -29
  155. pymisp/data/misp-objects/tools/adoc_objects.py +0 -145
  156. pymisp/data/misp-objects/tools/alfred_links_to_relarelationships.py +0 -48
  157. pymisp/data/misp-objects/tools/list_of_objects.py +0 -50
  158. pymisp/data/misp-objects/tools/updated.sh +0 -6
  159. pymisp/data/misp-objects/tools/validate_opposites.sh +0 -17
  160. pymisp/data/misp-objects/unique_uuid.py +0 -16
  161. pymisp/data/misp-objects/validate_all.sh +0 -38
  162. {pymisp-2.5.3.dist-info → pymisp-2.5.7.dist-info}/LICENSE +0 -0
@@ -0,0 +1,3734 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import logging
8
+ import os
9
+ import time
10
+ import unittest
11
+
12
+ from datetime import datetime, timedelta, date, timezone
13
+ from io import BytesIO
14
+ from pathlib import Path
15
+ from typing import TypeVar, Any
16
+ from uuid import uuid4
17
+
18
+ import urllib3
19
+
20
+ from pymisp.tools import make_binary_objects
21
+
22
+ try:
23
+ from pymisp import (register_user, PyMISP, MISPEvent, MISPOrganisation,
24
+ MISPUser, Distribution, ThreatLevel, Analysis, MISPObject,
25
+ MISPAttribute, MISPSighting, MISPShadowAttribute, MISPTag,
26
+ MISPSharingGroup, MISPFeed, MISPServer, MISPUserSetting,
27
+ MISPEventReport, MISPCorrelationExclusion, MISPGalaxyCluster,
28
+ MISPGalaxy, MISPOrganisationBlocklist, MISPEventBlocklist,
29
+ MISPNote, MISPRole)
30
+ from pymisp.tools import CSVLoader, DomainIPObject, ASNObject, GenericObjectGenerator
31
+ except ImportError:
32
+ raise
33
+
34
+ try:
35
+ from keys import url, key # type: ignore
36
+ verifycert = False
37
+ except ImportError as e:
38
+ print(e)
39
+ url = 'https://10.197.206.84'
40
+ key = 'OdzzuBSnH83tEjvZbf7SFejC1kC3gS11Cnj2wxLk'
41
+ verifycert = False
42
+
43
+ logging.disable(logging.CRITICAL)
44
+ logger = logging.getLogger('pymisp')
45
+
46
+ urllib3.disable_warnings()
47
+
48
+ fast_mode = False
49
+
50
+ test_file_path = Path('tests/viper-test-files')
51
+
52
+ print(test_file_path, 'exists: ', test_file_path.exists())
53
+
54
+ if not test_file_path.exists():
55
+ print('The test files are missing, pulling it.')
56
+ os.system('git clone https://github.com/viper-framework/viper-test-files.git tests/viper-test-files')
57
+
58
+ T = TypeVar('T', bound='TestComprehensive')
59
+
60
+
61
+ class TestComprehensive(unittest.TestCase):
62
+
63
+ admin_misp_connector: PyMISP
64
+ user_misp_connector: PyMISP
65
+ test_usr: MISPUser
66
+ test_pub: MISPUser
67
+ test_org: MISPOrganisation
68
+ test_org_delegate: MISPOrganisation
69
+ delegate_user_misp_connector: PyMISP
70
+ pub_misp_connector: PyMISP
71
+ test_usr_delegate: MISPUser
72
+
73
+ @classmethod
74
+ def setUpClass(cls: type[T]) -> None:
75
+ cls.maxDiff = None
76
+ # Connect as admin
77
+ cls.admin_misp_connector = PyMISP(url, key, verifycert, debug=False)
78
+ cls.admin_misp_connector.set_server_setting('Security.allow_self_registration', True, force=True)
79
+ cls.admin_misp_connector.set_server_setting('debug', 1, force=True)
80
+ if not fast_mode:
81
+ r = cls.admin_misp_connector.update_misp()
82
+ print(r)
83
+ # Creates an org
84
+ organisation = MISPOrganisation()
85
+ organisation.name = 'Test Org'
86
+ cls.test_org = cls.admin_misp_connector.add_organisation(organisation, pythonify=True) # type: ignore[assignment]
87
+ # Create an org to delegate to
88
+ organisation = MISPOrganisation()
89
+ organisation.name = 'Test Org - delegate'
90
+ cls.test_org_delegate = cls.admin_misp_connector.add_organisation(organisation, pythonify=True) # type: ignore[assignment]
91
+ # Set the refault role (id 3 on the VM)
92
+ cls.admin_misp_connector.set_default_role(3)
93
+ # Creates a user
94
+ user = MISPUser()
95
+ user.email = 'testusr@user.local'
96
+ user.org_id = cls.test_org.id
97
+ cls.test_usr = cls.admin_misp_connector.add_user(user, pythonify=True) # type: ignore[assignment]
98
+ cls.user_misp_connector = PyMISP(url, cls.test_usr.authkey, verifycert, debug=True)
99
+ cls.user_misp_connector.toggle_global_pythonify()
100
+ # Creates a publisher
101
+ user = MISPUser()
102
+ user.email = 'testpub@user.local'
103
+ user.org_id = cls.test_org.id
104
+ user.role_id = 4
105
+ cls.test_pub = cls.admin_misp_connector.add_user(user, pythonify=True) # type: ignore[assignment]
106
+ cls.pub_misp_connector = PyMISP(url, cls.test_pub.authkey, verifycert)
107
+ # Creates a user that can accept a delegation request
108
+ user = MISPUser()
109
+ user.email = 'testusr@delegate.recipient.local'
110
+ user.org_id = cls.test_org_delegate.id
111
+ user.role_id = 2
112
+ cls.test_usr_delegate = cls.admin_misp_connector.add_user(user, pythonify=True) # type: ignore[assignment]
113
+ cls.delegate_user_misp_connector = PyMISP(url, cls.test_usr_delegate.authkey, verifycert, debug=False)
114
+ cls.delegate_user_misp_connector.toggle_global_pythonify()
115
+ if not fast_mode:
116
+ # Update all json stuff
117
+ cls.admin_misp_connector.update_object_templates()
118
+ cls.admin_misp_connector.update_galaxies()
119
+ cls.admin_misp_connector.update_noticelists()
120
+ cls.admin_misp_connector.update_warninglists()
121
+ cls.admin_misp_connector.update_taxonomies()
122
+ cls.admin_misp_connector.load_default_feeds()
123
+
124
+ @classmethod
125
+ def tearDownClass(cls) -> None:
126
+ # Delete publisher
127
+ cls.admin_misp_connector.delete_user(cls.test_pub)
128
+ # Delete user
129
+ cls.admin_misp_connector.delete_user(cls.test_usr)
130
+ cls.admin_misp_connector.delete_user(cls.test_usr_delegate)
131
+ # Delete org
132
+ cls.admin_misp_connector.delete_organisation(cls.test_org)
133
+ cls.admin_misp_connector.delete_organisation(cls.test_org_delegate)
134
+
135
+ def create_simple_event(self, force_timestamps: bool=False) -> MISPEvent:
136
+ mispevent = MISPEvent(force_timestamps=force_timestamps)
137
+ mispevent.info = 'This is a super simple test'
138
+ mispevent.distribution = Distribution.your_organisation_only
139
+ mispevent.threat_level_id = ThreatLevel.low
140
+ mispevent.analysis = Analysis.completed
141
+ mispevent.add_attribute('text', str(uuid4()))
142
+ return mispevent
143
+
144
+ def environment(self) -> tuple[MISPEvent, MISPEvent, MISPEvent]:
145
+ first_event = MISPEvent()
146
+ first_event.info = 'First event - org only - low - completed'
147
+ first_event.distribution = Distribution.your_organisation_only
148
+ first_event.threat_level_id = ThreatLevel.low
149
+ first_event.analysis = Analysis.completed
150
+ first_event.set_date("2017-12-31")
151
+ first_event.add_attribute('text', 'FIRST_EVENT' + str(uuid4()))
152
+ first_event.attributes[0].add_tag('admin_only')
153
+ first_event.attributes[0].add_tag('tlp:white___test')
154
+ first_event.add_attribute('text', str(uuid4()))
155
+ first_event.attributes[1].add_tag('unique___test')
156
+
157
+ second_event = MISPEvent()
158
+ second_event.info = 'Second event - org only - medium - ongoing'
159
+ second_event.distribution = Distribution.your_organisation_only
160
+ second_event.threat_level_id = ThreatLevel.medium
161
+ second_event.analysis = Analysis.ongoing
162
+ second_event.set_date("Aug 18 2018")
163
+ second_event.add_attribute('text', 'SECOND_EVENT' + str(uuid4()))
164
+ second_event.attributes[0].add_tag('tlp:white___test')
165
+ second_event.add_attribute('ip-dst', '1.1.1.1')
166
+ second_event.attributes[1].add_tag('tlp:amber___test')
167
+ # Same value as in first event.
168
+ second_event.add_attribute('text', first_event.attributes[0].value)
169
+
170
+ third_event = MISPEvent()
171
+ third_event.info = 'Third event - all orgs - high - initial'
172
+ third_event.distribution = Distribution.all_communities
173
+ third_event.threat_level_id = ThreatLevel.high
174
+ third_event.analysis = Analysis.initial
175
+ third_event.set_date("Jun 25 2018")
176
+ third_event.add_tag('tlp:white___test')
177
+ third_event.add_attribute('text', 'THIRD_EVENT' + str(uuid4()))
178
+ third_event.attributes[0].add_tag('tlp:amber___test')
179
+ third_event.attributes[0].add_tag('foo_double___test')
180
+ third_event.add_attribute('ip-src', '8.8.8.8')
181
+ third_event.attributes[1].add_tag('tlp:amber___test')
182
+ third_event.add_attribute('ip-dst', '9.9.9.9')
183
+
184
+ # Create first and third event as admin
185
+ # usr won't be able to see the first one
186
+ first: MISPEvent = self.admin_misp_connector.add_event(first_event, pythonify=True) # type: ignore[assignment]
187
+ third: MISPEvent = self.admin_misp_connector.add_event(third_event, pythonify=True) # type: ignore[assignment]
188
+ # Create second event as user
189
+ second: MISPEvent = self.user_misp_connector.add_event(second_event) # type: ignore[assignment]
190
+ return first, second, third
191
+
192
+ def test_server_settings(self) -> None:
193
+ settings = self.admin_misp_connector.server_settings()
194
+ for final_setting in settings['finalSettings']:
195
+ if final_setting['setting'] == 'MISP.max_correlations_per_event':
196
+ self.assertEqual(final_setting['value'], 5000)
197
+ break
198
+ r = self.admin_misp_connector.set_server_setting('MISP.max_correlations_per_event', 10)
199
+ self.assertEqual(r['message'], 'Field updated', r)
200
+
201
+ setting = self.admin_misp_connector.get_server_setting('MISP.max_correlations_per_event')
202
+ self.assertEqual(setting['value'], 10)
203
+ r = self.admin_misp_connector.set_server_setting('MISP.max_correlations_per_event', 5000)
204
+ self.assertEqual(r['message'], 'Field updated', r)
205
+
206
+ setting = self.admin_misp_connector.get_server_setting('MISP.live')
207
+ self.assertTrue(setting['value'])
208
+ r = self.admin_misp_connector.set_server_setting('MISP.live', False, force=True)
209
+ self.assertEqual(r['message'], 'Field updated', r)
210
+ setting = self.admin_misp_connector.get_server_setting('MISP.live')
211
+ self.assertFalse(setting['value'])
212
+ r = self.admin_misp_connector.set_server_setting('MISP.live', True, force=True)
213
+ self.assertEqual(r['message'], 'Field updated', r)
214
+ setting = self.admin_misp_connector.get_server_setting('MISP.live')
215
+ self.assertTrue(setting['value'])
216
+
217
+ def test_search_value_event(self) -> None:
218
+ '''Search a value on the event controller
219
+ * Test ACL admin user vs normal user in an other org
220
+ * Make sure we have one match
221
+ '''
222
+ try:
223
+ first, second, third = self.environment()
224
+ # Search as admin
225
+ events: list[MISPEvent] = self.admin_misp_connector.search(value=first.attributes[0].value, pythonify=True) # type: ignore[assignment]
226
+ self.assertEqual(len(events), 2)
227
+ for e in events:
228
+ self.assertIn(e.id, [first.id, second.id])
229
+ # Search as user
230
+ events = self.user_misp_connector.search(value=first.attributes[0].value) # type: ignore[assignment]
231
+ self.assertEqual(len(events), 1)
232
+ for e in events:
233
+ self.assertIn(e.id, [second.id])
234
+ # Non-existing value
235
+ events = self.user_misp_connector.search(value=str(uuid4())) # type: ignore[assignment]
236
+ self.assertEqual(events, [])
237
+ finally:
238
+ # Delete events
239
+ self.admin_misp_connector.delete_event(first)
240
+ self.admin_misp_connector.delete_event(second)
241
+ self.admin_misp_connector.delete_event(third)
242
+
243
+ def test_search_value_attribute(self) -> None:
244
+ '''Search value in attributes controller'''
245
+ try:
246
+ first, second, third = self.environment()
247
+ # Search as admin
248
+ attributes: list[MISPAttribute] = self.admin_misp_connector.search(controller='attributes', value=first.attributes[0].value, pythonify=True) # type: ignore[assignment]
249
+ self.assertEqual(len(attributes), 2)
250
+ for a in attributes:
251
+ self.assertIn(a.event_id, [first.id, second.id])
252
+ # Search as user
253
+ attributes = self.user_misp_connector.search(controller='attributes', value=first.attributes[0].value) # type: ignore[assignment]
254
+ self.assertEqual(len(attributes), 1)
255
+ for a in attributes:
256
+ self.assertIn(a.event_id, [second.id])
257
+ # Non-existing value
258
+ attributes = self.user_misp_connector.search(controller='attributes', value=str(uuid4())) # type: ignore[assignment]
259
+ self.assertEqual(attributes, [])
260
+
261
+ # Include context - search as user (can only see one event)
262
+ attributes = self.user_misp_connector.search(controller='attributes', value=first.attributes[0].value, include_context=True, pythonify=True) # type: ignore[assignment]
263
+ self.assertTrue(isinstance(attributes[0].Event, MISPEvent))
264
+ self.assertEqual(attributes[0].Event.uuid, second.uuid)
265
+
266
+ # Include context - search as admin (can see both event)
267
+ attributes = self.admin_misp_connector.search(controller='attributes', value=first.attributes[0].value, include_context=True, pythonify=True) # type: ignore[assignment]
268
+ self.assertTrue(isinstance(attributes[0].Event, MISPEvent))
269
+ self.assertEqual(attributes[0].Event.uuid, first.uuid)
270
+ self.assertEqual(attributes[1].Event.uuid, second.uuid)
271
+
272
+ # Include correlations - search as admin (can see both event)
273
+ attributes = self.admin_misp_connector.search(controller='attributes', value=first.attributes[0].value, include_correlations=True, pythonify=True) # type: ignore[assignment]
274
+ self.assertTrue(isinstance(attributes[0].Event, MISPEvent))
275
+ self.assertEqual(attributes[0].Event.uuid, first.uuid)
276
+ self.assertEqual(attributes[1].Event.uuid, second.uuid)
277
+ self.assertEqual(attributes[0].RelatedAttribute[0].Event.uuid, second.uuid)
278
+ self.assertEqual(attributes[1].RelatedAttribute[0].Event.uuid, first.uuid)
279
+
280
+ # Include sightings - search as admin (can see both event)
281
+ s: dict[str, Any] = {'value': first.attributes[0].value}
282
+ self.admin_misp_connector.add_sighting(s)
283
+ attributes = self.admin_misp_connector.search(controller='attributes', value=first.attributes[0].value, include_sightings=True, pythonify=True) # type: ignore[assignment]
284
+ self.assertTrue(isinstance(attributes[0].Event, MISPEvent))
285
+ self.assertEqual(attributes[0].Event.uuid, first.uuid)
286
+ self.assertEqual(attributes[1].Event.uuid, second.uuid)
287
+ self.assertTrue(isinstance(attributes[0].Sighting[0], MISPSighting))
288
+
289
+ finally:
290
+ # Delete event
291
+ self.admin_misp_connector.delete_event(first)
292
+ self.admin_misp_connector.delete_event(second)
293
+ self.admin_misp_connector.delete_event(third)
294
+
295
+ def test_search_type_event(self) -> None:
296
+ '''Search multiple events, search events containing attributes with specific types'''
297
+ try:
298
+ first, second, third = self.environment()
299
+ # Search as admin
300
+ if isinstance(first.timestamp, datetime):
301
+ ts = first.timestamp.timestamp()
302
+ else:
303
+ ts = first.timestamp
304
+ events: list[MISPEvent] = self.admin_misp_connector.search(timestamp=ts, pythonify=True) # type: ignore[assignment]
305
+ self.assertEqual(len(events), 3)
306
+ for e in events:
307
+ self.assertIn(e.id, [first.id, second.id, third.id])
308
+ attributes_types_search = self.admin_misp_connector.build_complex_query(or_parameters=['ip-src', 'ip-dst'])
309
+ events = self.admin_misp_connector.search(timestamp=ts, # type: ignore[assignment,type-var]
310
+ type_attribute=attributes_types_search, pythonify=True)
311
+ self.assertEqual(len(events), 2)
312
+ for e in events:
313
+ self.assertIn(e.id, [second.id, third.id])
314
+ finally:
315
+ # Delete event
316
+ self.admin_misp_connector.delete_event(first)
317
+ self.admin_misp_connector.delete_event(second)
318
+ self.admin_misp_connector.delete_event(third)
319
+
320
+ def test_search_index(self) -> None:
321
+ try:
322
+ first, second, third = self.environment()
323
+ # Search as admin
324
+ if isinstance(first.timestamp, datetime):
325
+ ts = first.timestamp.timestamp()
326
+ else:
327
+ ts = first.timestamp
328
+ events: MISPEvent = self.admin_misp_connector.search_index(timestamp=ts, pythonify=True) # type: ignore[assignment]
329
+ self.assertEqual(len(events), 3)
330
+ for e in events:
331
+ self.assertIn(e.id, [first.id, second.id, third.id])
332
+
333
+ # Test limit and pagination
334
+ event_one: MISPEvent = self.admin_misp_connector.search_index(timestamp=ts, limit=1, page=1, pythonify=True)[0] # type: ignore[index,assignment]
335
+ event_two: MISPEvent = self.admin_misp_connector.search_index(timestamp=ts, limit=1, page=2, pythonify=True)[0] # type: ignore[index,assignment]
336
+ self.assertTrue(event_one.id != event_two.id)
337
+ two_events = self.admin_misp_connector.search_index(limit=2)
338
+ self.assertTrue(len(two_events), 2)
339
+
340
+ # Test ordering by the Info field. Can't use timestamp as each will likely have the same
341
+ event: MISPEvent = self.admin_misp_connector.search_index(timestamp=ts, sort="info", desc=True, limit=1, pythonify=True)[0] # type: ignore[index,assignment]
342
+ # First|Second|*Third* event
343
+ self.assertEqual(event.id, third.id)
344
+ # *First*|Second|Third event
345
+ event = self.admin_misp_connector.search_index(timestamp=ts, sort="info", desc=False, limit=1, pythonify=True)[0] # type: ignore[index,assignment]
346
+ self.assertEqual(event.id, first.id)
347
+ finally:
348
+ # Delete event
349
+ self.admin_misp_connector.delete_event(first)
350
+ self.admin_misp_connector.delete_event(second)
351
+ self.admin_misp_connector.delete_event(third)
352
+
353
+ def test_search_objects(self) -> None:
354
+ '''Search for objects'''
355
+ try:
356
+ first = self.create_simple_event()
357
+ obj = MISPObject('file')
358
+ obj.add_attribute('filename', 'foo')
359
+ first.add_object(obj)
360
+ first = self.user_misp_connector.add_event(first)
361
+ logger = logging.getLogger('pymisp')
362
+ logger.setLevel(logging.DEBUG)
363
+ objects = self.user_misp_connector.search(controller='objects',
364
+ object_name='file', pythonify=True)
365
+ self.assertEqual(len(objects), 1)
366
+ self.assertEqual(objects[0].attributes[0].value, 'foo')
367
+ finally:
368
+ # Delete event
369
+ self.admin_misp_connector.delete_event(first)
370
+
371
+ def test_search_type_attribute(self) -> None:
372
+ '''Search multiple attributes, search attributes with specific types'''
373
+ try:
374
+ first, second, third = self.environment()
375
+ # Search as admin
376
+ attributes = self.admin_misp_connector.search(controller='attributes',
377
+ timestamp=first.timestamp.timestamp(), pythonify=True)
378
+ self.assertEqual(len(attributes), 8)
379
+ for a in attributes:
380
+ self.assertIn(a.event_id, [first.id, second.id, third.id])
381
+ # Search as user
382
+ attributes_types_search = self.admin_misp_connector.build_complex_query(or_parameters=['ip-src', 'ip-dst'])
383
+ attributes = self.admin_misp_connector.search(controller='attributes',
384
+ timestamp=first.timestamp.timestamp(),
385
+ type_attribute=attributes_types_search, pythonify=True)
386
+ self.assertEqual(len(attributes), 3)
387
+ for a in attributes:
388
+ self.assertIn(a.event_id, [second.id, third.id])
389
+ finally:
390
+ # Delete event
391
+ self.admin_misp_connector.delete_event(first)
392
+ self.admin_misp_connector.delete_event(second)
393
+ self.admin_misp_connector.delete_event(third)
394
+
395
+ def test_search_tag_event(self) -> None:
396
+ '''Search Tags at events level'''
397
+ try:
398
+ first, second, third = self.environment()
399
+ # Search as admin
400
+ events = self.admin_misp_connector.search(tags='tlp:white___test', pythonify=True)
401
+ self.assertEqual(len(events), 3)
402
+ for e in events:
403
+ self.assertIn(e.id, [first.id, second.id, third.id])
404
+ events = self.admin_misp_connector.search(tags='tlp:amber___test', pythonify=True)
405
+ self.assertEqual(len(events), 2)
406
+ for e in events:
407
+ self.assertIn(e.id, [second.id, third.id])
408
+ events = self.admin_misp_connector.search(tags='admin_only', pythonify=True)
409
+ self.assertEqual(len(events), 1)
410
+ for e in events:
411
+ self.assertIn(e.id, [first.id])
412
+ # Search as user
413
+ events = self.user_misp_connector.search(tags='tlp:white___test')
414
+ self.assertEqual(len(events), 2)
415
+ for e in events:
416
+ self.assertIn(e.id, [second.id, third.id])
417
+ events = self.user_misp_connector.search(tags='tlp:amber___test')
418
+ self.assertEqual(len(events), 2)
419
+ for e in events:
420
+ self.assertIn(e.id, [second.id, third.id])
421
+ events = self.user_misp_connector.search(tags='admin_only')
422
+ self.assertEqual(events, [])
423
+ finally:
424
+ # Delete event
425
+ self.admin_misp_connector.delete_event(first)
426
+ self.admin_misp_connector.delete_event(second)
427
+ self.admin_misp_connector.delete_event(third)
428
+
429
+ def test_search_tag_attribute(self) -> None:
430
+ '''Search Tags at attributes level'''
431
+ try:
432
+ first, second, third = self.environment()
433
+ # Search as admin
434
+ attributes = self.admin_misp_connector.search(controller='attributes', tags='tlp:white___test', pythonify=True)
435
+ self.assertEqual(len(attributes), 5)
436
+ attributes = self.admin_misp_connector.search(controller='attributes', tags='tlp:amber___test', pythonify=True)
437
+ self.assertEqual(len(attributes), 3)
438
+ attributes = self.admin_misp_connector.search(tags='admin_only', pythonify=True)
439
+ self.assertEqual(len(attributes), 1)
440
+ # Search as user
441
+ attributes = self.user_misp_connector.search(controller='attributes', tags='tlp:white___test')
442
+ self.assertEqual(len(attributes), 4)
443
+ attributes = self.user_misp_connector.search(controller='attributes', tags='tlp:amber___test')
444
+ self.assertEqual(len(attributes), 3)
445
+ attributes = self.user_misp_connector.search(tags='admin_only')
446
+ self.assertEqual(attributes, [])
447
+ attributes_tags_search = self.admin_misp_connector.build_complex_query(or_parameters=['tlp:amber___test'], not_parameters=['tlp:white___test'])
448
+ attributes = self.user_misp_connector.search(controller='attributes', tags=attributes_tags_search)
449
+ self.assertEqual(len(attributes), 1)
450
+ finally:
451
+ # Delete event
452
+ self.admin_misp_connector.delete_event(first)
453
+ self.admin_misp_connector.delete_event(second)
454
+ self.admin_misp_connector.delete_event(third)
455
+
456
+ def test_search_tag_advanced_event(self) -> None:
457
+ '''Advanced search Tags at events level'''
458
+ try:
459
+ first, second, third = self.environment()
460
+ complex_query = self.admin_misp_connector.build_complex_query(or_parameters=['tlp:white___test'],
461
+ not_parameters=['tlp:amber___test',
462
+ 'foo_double___test'])
463
+ events = self.admin_misp_connector.search(tags=complex_query, pythonify=True)
464
+ self.assertEqual(len(events), 3)
465
+ for e in events:
466
+ self.assertIn(e.id, [first.id, second.id, third.id])
467
+ for a in e.attributes:
468
+ self.assertEqual([t for t in a.tags if t.name == 'tlp:amber___test'], [])
469
+ for a in e.attributes:
470
+ self.assertEqual([t for t in a.tags if t.name == 'foo_double___test'], [])
471
+
472
+ complex_query = self.admin_misp_connector.build_complex_query(or_parameters=['unique___test'],
473
+ not_parameters=['tlp:white___test'])
474
+ events = self.admin_misp_connector.search(tags=complex_query, pythonify=True)
475
+ self.assertEqual(len(events), 1)
476
+ for e in events:
477
+ self.assertIn(e.id, [first.id, second.id])
478
+ for a in e.attributes:
479
+ self.assertEqual([t for t in a.tags if t.name == 'tlp:white___test'], [])
480
+ finally:
481
+ # Delete event
482
+ self.admin_misp_connector.delete_event(first)
483
+ self.admin_misp_connector.delete_event(second)
484
+ self.admin_misp_connector.delete_event(third)
485
+
486
+ def test_search_tag_advanced_attributes(self) -> None:
487
+ '''Advanced search Tags at attributes level'''
488
+ try:
489
+ first, second, third = self.environment()
490
+ complex_query = self.admin_misp_connector.build_complex_query(or_parameters=['tlp:white___test'],
491
+ not_parameters=['tlp:amber___test',
492
+ 'foo_double___test'])
493
+ attributes = self.admin_misp_connector.search(controller='attributes', tags=complex_query, pythonify=True)
494
+ self.assertEqual(len(attributes), 3)
495
+ for a in attributes:
496
+ self.assertEqual([t for t in a.tags if t.name == 'tlp:amber___test'], [])
497
+ for a in attributes:
498
+ self.assertEqual([t for t in a.tags if t.name == 'foo_double___test'], [])
499
+ finally:
500
+ # Delete event
501
+ self.admin_misp_connector.delete_event(first)
502
+ self.admin_misp_connector.delete_event(second)
503
+ self.admin_misp_connector.delete_event(third)
504
+
505
+ def test_search_timestamp_event(self) -> None:
506
+ '''Search specific update timestamps at events level'''
507
+ # Creating event 1 - timestamp 5 min ago
508
+ first = self.create_simple_event(force_timestamps=True)
509
+ event_creation_timestamp_first = datetime.now() - timedelta(minutes=5)
510
+ first.timestamp = event_creation_timestamp_first
511
+ # Creating event 2 - timestamp 2 min ago
512
+ second = self.create_simple_event(force_timestamps=True)
513
+ event_creation_timestamp_second = datetime.now() - timedelta(minutes=2)
514
+ second.timestamp = event_creation_timestamp_second
515
+ try:
516
+ first = self.user_misp_connector.add_event(first)
517
+ second = self.user_misp_connector.add_event(second)
518
+ # Search as user
519
+ # # Test - last 4 min
520
+ events = self.user_misp_connector.search(timestamp='4m')
521
+ self.assertEqual(len(events), 1)
522
+ self.assertEqual(events[0].id, second.id)
523
+ self.assertEqual(events[0].timestamp.timestamp(), int(event_creation_timestamp_second.timestamp()))
524
+
525
+ # # Test timestamp of 2nd event
526
+ events = self.user_misp_connector.search(timestamp=event_creation_timestamp_second.timestamp())
527
+ self.assertEqual(len(events), 1)
528
+ self.assertEqual(events[0].id, second.id)
529
+ self.assertEqual(events[0].timestamp.timestamp(), int(event_creation_timestamp_second.timestamp()))
530
+
531
+ # # Test interval -6 min -> -4 min
532
+ events = self.user_misp_connector.search(timestamp=['6m', '4m'])
533
+ self.assertEqual(len(events), 1)
534
+ self.assertEqual(events[0].id, first.id)
535
+ self.assertEqual(events[0].timestamp.timestamp(), int(event_creation_timestamp_first.timestamp()))
536
+ finally:
537
+ # Delete event
538
+ self.admin_misp_connector.delete_event(first)
539
+ self.admin_misp_connector.delete_event(second)
540
+
541
+ def test_search_timestamp_attribute(self) -> None:
542
+ '''Search specific update timestamps at attributes level'''
543
+ # Creating event 1 - timestamp 5 min ago
544
+ first = self.create_simple_event(force_timestamps=True)
545
+ event_creation_timestamp_first = datetime.now() - timedelta(minutes=5)
546
+ first.timestamp = event_creation_timestamp_first
547
+ first.attributes[0].timestamp = event_creation_timestamp_first
548
+ # Creating event 2 - timestamp 2 min ago
549
+ second = self.create_simple_event(force_timestamps=True)
550
+ event_creation_timestamp_second = datetime.now() - timedelta(minutes=2)
551
+ second.timestamp = event_creation_timestamp_second
552
+ second.attributes[0].timestamp = event_creation_timestamp_second
553
+ try:
554
+ first = self.user_misp_connector.add_event(first)
555
+ second = self.user_misp_connector.add_event(second)
556
+ # Search as user
557
+ # # Test - last 4 min
558
+ attributes = self.user_misp_connector.search(controller='attributes', timestamp='4m')
559
+ self.assertEqual(len(attributes), 1)
560
+ self.assertEqual(attributes[0].event_id, second.id)
561
+ self.assertEqual(attributes[0].timestamp.timestamp(), int(event_creation_timestamp_second.timestamp()))
562
+
563
+ # # Test timestamp of 2nd event
564
+ attributes = self.user_misp_connector.search(controller='attributes', timestamp=event_creation_timestamp_second.timestamp())
565
+ self.assertEqual(len(attributes), 1)
566
+ self.assertEqual(attributes[0].event_id, second.id)
567
+ self.assertEqual(attributes[0].timestamp.timestamp(), int(event_creation_timestamp_second.timestamp()))
568
+
569
+ # # Test interval -6 min -> -4 min
570
+ attributes = self.user_misp_connector.search(controller='attributes', timestamp=['6m', '4m'])
571
+ self.assertEqual(len(attributes), 1)
572
+ self.assertEqual(attributes[0].event_id, first.id)
573
+ self.assertEqual(attributes[0].timestamp.timestamp(), int(event_creation_timestamp_first.timestamp()))
574
+ finally:
575
+ # Delete event
576
+ self.admin_misp_connector.delete_event(first)
577
+ self.admin_misp_connector.delete_event(second)
578
+
579
+ def test_user_perms(self) -> None:
580
+ '''Test publish rights'''
581
+ try:
582
+ first = self.create_simple_event()
583
+ first.publish()
584
+ # Add event as user, no publish rights
585
+ first = self.user_misp_connector.add_event(first)
586
+ self.assertFalse(first.published)
587
+ # Add event as publisher
588
+ first.publish()
589
+ first = self.pub_misp_connector.update_event(first, pythonify=True)
590
+ self.assertTrue(first.published)
591
+ finally:
592
+ # Delete event
593
+ self.admin_misp_connector.delete_event(first)
594
+
595
+ def test_delete_with_update(self) -> None:
596
+ try:
597
+ first = self.create_simple_event()
598
+ obj = MISPObject('file')
599
+ obj.add_attribute('filename', 'foo')
600
+ first.add_object(obj)
601
+ first = self.user_misp_connector.add_event(first)
602
+
603
+ first.attributes[0].deleted = True
604
+ deleted_attribute = self.user_misp_connector.update_attribute(first.attributes[0], pythonify=True)
605
+ self.assertTrue(deleted_attribute.deleted)
606
+
607
+ first.objects[0].deleted = True
608
+ deleted_object = self.user_misp_connector.update_object(first.objects[0], pythonify=True)
609
+ self.assertTrue(deleted_object.deleted)
610
+
611
+ # Get event with deleted entries
612
+ first = self.user_misp_connector.get_event(first, deleted=True, pythonify=True)
613
+ self.assertTrue(first.attributes[0].deleted)
614
+ self.assertTrue(first.objects[0].deleted)
615
+
616
+ finally:
617
+ # Delete event
618
+ self.admin_misp_connector.delete_event(first)
619
+
620
+ def test_get_non_exists_event(self) -> None:
621
+ event = self.user_misp_connector.get_event(0) # non exists id
622
+ self.assertEqual(event['errors'][0], 404)
623
+
624
+ event = self.user_misp_connector.get_event("ab2b6e28-fda5-4282-bf60-22b81de77851") # non exists uuid
625
+ self.assertEqual(event['errors'][0], 404)
626
+
627
+ def test_delete_by_uuid(self) -> None:
628
+ try:
629
+ first = self.create_simple_event()
630
+ obj = MISPObject('file')
631
+ obj.add_attribute('filename', 'foo')
632
+ first.add_object(obj)
633
+ obj = MISPObject('file')
634
+ obj.add_attribute('filename', 'bar')
635
+ first.add_object(obj)
636
+ first = self.user_misp_connector.add_event(first)
637
+ r = self.user_misp_connector.delete_attribute(first.attributes[0].uuid)
638
+ self.assertEqual(r['message'], 'Attribute deleted.')
639
+ r = self.user_misp_connector.delete_object(first.objects[0].uuid)
640
+ self.assertEqual(r['message'], 'Object deleted')
641
+ # Test deleted search
642
+ r = self.user_misp_connector.search(event_id=first.id, deleted=[0, 1], pythonify=True)
643
+ self.assertTrue(isinstance(r[0], MISPEvent))
644
+ self.assertEqual(len(r[0].objects), 2)
645
+ self.assertTrue(r[0].objects[0].deleted)
646
+ self.assertFalse(r[0].objects[1].deleted)
647
+ self.assertEqual(len(r[0].attributes), 1)
648
+ self.assertTrue(r[0].attributes[0].deleted)
649
+ # Test deleted get
650
+ r = self.user_misp_connector.get_event(first, deleted=True, pythonify=True)
651
+ self.assertTrue(isinstance(r, MISPEvent))
652
+ self.assertEqual(len(r.objects), 2)
653
+ self.assertTrue(r.objects[0].deleted)
654
+ self.assertFalse(r.objects[1].deleted)
655
+ self.assertEqual(len(r.attributes), 1)
656
+ self.assertTrue(r.attributes[0].deleted)
657
+
658
+ r = self.user_misp_connector.delete_event(first.uuid)
659
+ self.assertEqual(r['message'], 'Event deleted.')
660
+ finally:
661
+ # Delete event
662
+ self.admin_misp_connector.delete_event(first)
663
+
664
+ def test_search_publish_timestamp(self) -> None:
665
+ '''Search for a specific publication timestamp, an interval, and invalid values.'''
666
+ # Creating event 1
667
+ first = self.create_simple_event()
668
+ first.publish()
669
+ # Creating event 2
670
+ second = self.create_simple_event()
671
+ second.publish()
672
+ try:
673
+ first = self.pub_misp_connector.add_event(first, pythonify=True)
674
+ time.sleep(10)
675
+ second = self.pub_misp_connector.add_event(second, pythonify=True)
676
+ # Test invalid query
677
+ events = self.pub_misp_connector.search(publish_timestamp='5x', pythonify=True)
678
+ self.assertEqual(events, [])
679
+ events = self.pub_misp_connector.search(publish_timestamp='ad', pythonify=True)
680
+ self.assertEqual(events, [])
681
+ events = self.pub_misp_connector.search(publish_timestamp='aaad', pythonify=True)
682
+ self.assertEqual(events, [])
683
+ # Test - last 4 min
684
+ events = self.pub_misp_connector.search(publish_timestamp='5s', pythonify=True)
685
+ self.assertEqual(len(events), 1)
686
+ self.assertEqual(events[0].id, second.id)
687
+
688
+ # Test 5 sec before timestamp of 2nd event
689
+ events = self.pub_misp_connector.search(publish_timestamp=(second.publish_timestamp.timestamp()), pythonify=True)
690
+ self.assertEqual(len(events), 1)
691
+ self.assertEqual(events[0].id, second.id)
692
+
693
+ # Test interval -6 min -> -4 min
694
+ events = self.pub_misp_connector.search(publish_timestamp=[first.publish_timestamp.timestamp() - 5,
695
+ second.publish_timestamp.timestamp() - 5], pythonify=True)
696
+ self.assertEqual(len(events), 1)
697
+ self.assertEqual(events[0].id, first.id)
698
+ finally:
699
+ # Delete event
700
+ self.admin_misp_connector.delete_event(first)
701
+ self.admin_misp_connector.delete_event(second)
702
+
703
+ def test_search_decay(self) -> None:
704
+ # Creating event 1
705
+ first = self.create_simple_event()
706
+ first.add_attribute('ip-dst', '8.8.8.8')
707
+ first.publish()
708
+ try:
709
+ r = self.admin_misp_connector.update_decaying_models()
710
+ self.assertTrue(r['success'], r)
711
+ simple_decaying_model = None
712
+ models = self.admin_misp_connector.decaying_models(pythonify=True)
713
+ for model in models:
714
+ if model.name == 'NIDS Simple Decaying Model':
715
+ simple_decaying_model = model
716
+ self.assertTrue(simple_decaying_model, models)
717
+ self.admin_misp_connector.enable_decaying_model(simple_decaying_model)
718
+ # TODO: check the response, it is curently an empty list
719
+ first = self.pub_misp_connector.add_event(first, pythonify=True)
720
+ result = self.pub_misp_connector.search('attributes', to_ids=1, includeDecayScore=True, pythonify=True)
721
+ self.assertTrue(result[0].decay_score, result[0].to_json(indent=2))
722
+ self.admin_misp_connector.disable_decaying_model(simple_decaying_model)
723
+ # TODO: check the response, it is curently a list of all the models
724
+ finally:
725
+ # Delete event
726
+ self.admin_misp_connector.delete_event(first)
727
+
728
+ def test_default_distribution(self) -> None:
729
+ '''The default distributions on the VM are This community only for the events and Inherit from event for attr/obj)'''
730
+ first = self.create_simple_event()
731
+ del first.distribution
732
+ o = first.add_object(name='file')
733
+ o.add_attribute('filename', value='foo.exe')
734
+ try:
735
+ # Event create
736
+ first = self.user_misp_connector.add_event(first)
737
+ self.assertEqual(first.distribution, Distribution.this_community_only.value)
738
+ self.assertEqual(first.attributes[0].distribution, Distribution.inherit.value)
739
+ self.assertEqual(first.objects[0].distribution, Distribution.inherit.value)
740
+ self.assertEqual(first.objects[0].attributes[0].distribution, Distribution.inherit.value)
741
+ # Event edit
742
+ first.add_attribute('ip-dst', '12.54.76.43')
743
+ o = first.add_object(name='file')
744
+ o.add_attribute('filename', value='foo2.exe')
745
+ first = self.user_misp_connector.update_event(first)
746
+ self.assertEqual(first.attributes[1].distribution, Distribution.inherit.value)
747
+ self.assertEqual(first.objects[1].distribution, Distribution.inherit.value)
748
+ self.assertEqual(first.objects[1].attributes[0].distribution, Distribution.inherit.value)
749
+ # Attribute create
750
+ attribute = self.user_misp_connector.add_attribute(first, {'type': 'comment', 'value': 'bar'})
751
+ self.assertEqual(attribute.value, 'bar', attribute.to_json())
752
+ self.assertEqual(attribute.distribution, Distribution.inherit.value, attribute.to_json())
753
+ # Object - add
754
+ o = MISPObject('file')
755
+ o.add_attribute('filename', value='blah.exe')
756
+ new_obj = self.user_misp_connector.add_object(first, o)
757
+ self.assertEqual(new_obj.distribution, int(Distribution.inherit.value))
758
+ self.assertEqual(new_obj.attributes[0].distribution, int(Distribution.inherit.value))
759
+ # Object - edit
760
+ clean_obj = MISPObject(name=new_obj.name, strict=True)
761
+ clean_obj.from_dict(**new_obj)
762
+ clean_obj.add_attribute('filename', value='blah.exe')
763
+ new_obj = self.user_misp_connector.update_object(clean_obj)
764
+ for a in new_obj.attributes:
765
+ self.assertEqual(a.distribution, int(Distribution.inherit.value))
766
+ finally:
767
+ # Delete event
768
+ self.admin_misp_connector.delete_event(first)
769
+
770
+ def test_exists(self) -> None:
771
+ """Check event, attribute and object existence"""
772
+ event = self.create_simple_event()
773
+ misp_object = MISPObject('domain-ip')
774
+ attribute = misp_object.add_attribute('domain', value='google.fr')
775
+ misp_object.add_attribute('ip', value='8.8.8.8')
776
+ event.add_object(misp_object)
777
+
778
+ # Event, attribute and object should not exists before event deletion
779
+ self.assertFalse(self.user_misp_connector.event_exists(event))
780
+ self.assertFalse(self.user_misp_connector.attribute_exists(attribute))
781
+ self.assertFalse(self.user_misp_connector.object_exists(misp_object))
782
+
783
+ try:
784
+ event = self.user_misp_connector.add_event(event, pythonify=True)
785
+ misp_object = event.objects[0]
786
+ attribute = misp_object.attributes[0]
787
+ self.assertTrue(self.user_misp_connector.event_exists(event))
788
+ self.assertTrue(self.user_misp_connector.event_exists(event.uuid))
789
+ self.assertTrue(self.user_misp_connector.event_exists(event.id))
790
+ self.assertTrue(self.user_misp_connector.attribute_exists(attribute))
791
+ self.assertTrue(self.user_misp_connector.attribute_exists(attribute.uuid))
792
+ self.assertTrue(self.user_misp_connector.attribute_exists(attribute.id))
793
+ self.assertTrue(self.user_misp_connector.object_exists(misp_object))
794
+ self.assertTrue(self.user_misp_connector.object_exists(misp_object.id))
795
+ self.assertTrue(self.user_misp_connector.object_exists(misp_object.uuid))
796
+ finally:
797
+ self.admin_misp_connector.delete_event(event)
798
+
799
+ # Event, attribute and object should not exists after event deletion
800
+ self.assertFalse(self.user_misp_connector.event_exists(event))
801
+ self.assertFalse(self.user_misp_connector.event_exists(event.id))
802
+ self.assertFalse(self.user_misp_connector.attribute_exists(attribute))
803
+ self.assertFalse(self.user_misp_connector.attribute_exists(attribute.id))
804
+ self.assertFalse(self.user_misp_connector.object_exists(misp_object))
805
+ self.assertFalse(self.user_misp_connector.object_exists(misp_object.id))
806
+
807
+ def test_simple_event(self) -> None:
808
+ '''Search a bunch of parameters:
809
+ * Value not existing
810
+ * only return metadata
811
+ * published yes/no
812
+ * event id
813
+ * uuid
814
+ * creator org
815
+ * substring search in value and eventinfo
816
+ * quickfilter
817
+ * date_from
818
+ * date_to
819
+ * deleted
820
+ * to_ids
821
+ * include_event_uuid
822
+ warning list
823
+ '''
824
+ first = self.create_simple_event()
825
+ first.info = 'foo bar blah'
826
+ # First has one text attribute
827
+ second = self.create_simple_event()
828
+ second.info = 'foo blah'
829
+ second.add_tag('tlp:amber___test')
830
+ second.set_date('2018-09-01')
831
+ second.add_attribute('ip-src', '8.8.8.8')
832
+ # second has two attributes: text and ip-src
833
+ try:
834
+ first = self.user_misp_connector.add_event(first)
835
+ second = self.user_misp_connector.add_event(second)
836
+ timeframe = [first.timestamp.timestamp() - 5, first.timestamp.timestamp() + 5]
837
+ # Search event we just created in multiple ways. Make sure it doesn't catch it when it shouldn't
838
+ events = self.user_misp_connector.search(timestamp=timeframe)
839
+ self.assertEqual(len(events), 2)
840
+ self.assertEqual(events[0].id, first.id)
841
+ self.assertEqual(events[1].id, second.id)
842
+ events = self.user_misp_connector.search(timestamp=timeframe, value='nothere')
843
+ self.assertEqual(events, [])
844
+ events = self.user_misp_connector.search(timestamp=timeframe, value=first.attributes[0].value)
845
+ self.assertEqual(len(events), 1)
846
+ self.assertEqual(events[0].id, first.id)
847
+ events = self.user_misp_connector.search(timestamp=[first.timestamp.timestamp() - 50,
848
+ first.timestamp.timestamp() - 10],
849
+ value=first.attributes[0].value, pythonify=True)
850
+ self.assertEqual(events, [])
851
+
852
+ # Test return content
853
+ events = self.user_misp_connector.search(timestamp=timeframe, metadata=False)
854
+ self.assertEqual(len(events), 2)
855
+ self.assertEqual(len(events[0].attributes), 1)
856
+ self.assertEqual(len(events[1].attributes), 2)
857
+ events = self.user_misp_connector.search(timestamp=timeframe, metadata=True)
858
+ self.assertEqual(len(events), 2)
859
+ self.assertEqual(len(events[0].attributes), 0)
860
+ self.assertEqual(len(events[1].attributes), 0)
861
+
862
+ # other things
863
+ events = self.user_misp_connector.search(timestamp=timeframe, published=True)
864
+ self.assertEqual(events, [])
865
+ events = self.user_misp_connector.search(timestamp=timeframe, published=False)
866
+ self.assertEqual(len(events), 2)
867
+ # check publish & search
868
+ bg_processing_state = self.admin_misp_connector.get_server_setting('MISP.background_jobs')['value']
869
+ self.admin_misp_connector.set_server_setting('MISP.background_jobs', False, force=True)
870
+ publish_result = self.admin_misp_connector.publish(second)
871
+ self.assertEqual(publish_result["success"], True)
872
+ second = self.admin_misp_connector.get_event(second, pythonify=True)
873
+ # check if the publishing succeeded
874
+ time.sleep(1)
875
+ self.assertEqual(second.published, True)
876
+ self.admin_misp_connector.set_server_setting('MISP.background_jobs', bg_processing_state, force=True)
877
+ events = self.user_misp_connector.search(timestamp=timeframe, published=False)
878
+ self.assertEqual(len(events), 1)
879
+
880
+ events = self.user_misp_connector.search(eventid=first.id)
881
+ self.assertEqual(len(events), 1)
882
+ self.assertEqual(events[0].id, first.id)
883
+ events = self.user_misp_connector.search(uuid=first.uuid)
884
+ self.assertEqual(len(events), 1)
885
+ self.assertEqual(events[0].id, first.id)
886
+ events = self.user_misp_connector.search(org=first.orgc_id)
887
+ self.assertEqual(len(events), 2)
888
+
889
+ # test like search
890
+ events = self.user_misp_connector.search(timestamp=timeframe, value='%{}%'.format(first.attributes[0].value.split('-')[2]))
891
+ self.assertEqual(len(events), 1)
892
+ self.assertEqual(events[0].id, first.id)
893
+ events = self.user_misp_connector.search(timestamp=timeframe, eventinfo='%bar blah%')
894
+ self.assertEqual(len(events), 1)
895
+ self.assertEqual(events[0].id, first.id)
896
+
897
+ # quickfilter
898
+ events = self.user_misp_connector.search(timestamp=timeframe,
899
+ quickfilter='%foo blah%', pythonify=True)
900
+ # FIXME: should return one event
901
+ # print(events)
902
+ # self.assertEqual(len(events), 1)
903
+ # self.assertEqual(events[0].id, second.id)
904
+
905
+ # date_from / date_to
906
+ events = self.user_misp_connector.search(timestamp=timeframe, date_from=date.today().isoformat())
907
+ self.assertEqual(len(events), 1)
908
+ self.assertEqual(events[0].id, first.id)
909
+ events = self.user_misp_connector.search(timestamp=timeframe, date_from='2018-09-01')
910
+ self.assertEqual(len(events), 2)
911
+ events = self.user_misp_connector.search(timestamp=timeframe, date_from='2018-09-01', date_to='2018-09-02')
912
+ self.assertEqual(len(events), 1)
913
+ self.assertEqual(events[0].id, second.id)
914
+
915
+ # Category
916
+ events = self.user_misp_connector.search(timestamp=timeframe, category='Network activity')
917
+ self.assertEqual(len(events), 1)
918
+ self.assertEqual(events[0].id, second.id)
919
+
920
+ # toids
921
+ events = self.user_misp_connector.search(timestamp=timeframe, to_ids='0')
922
+ self.assertEqual(len(events), 2)
923
+ events = self.user_misp_connector.search(timestamp=timeframe, to_ids='1')
924
+ self.assertEqual(len(events), 1)
925
+ self.assertEqual(events[0].id, second.id)
926
+ self.assertEqual(len(events[0].attributes), 1)
927
+
928
+ # deleted
929
+ second.attributes[1].delete()
930
+ self.user_misp_connector.update_event(second)
931
+ events = self.user_misp_connector.search(eventid=second.id)
932
+ self.assertEqual(len(events[0].attributes), 1)
933
+ events = self.user_misp_connector.search(eventid=second.id, deleted=True)
934
+ self.assertEqual(len(events[0].attributes), 1)
935
+
936
+ # include_event_uuid
937
+ attributes = self.user_misp_connector.search(controller='attributes', eventid=second.id, include_event_uuid=True)
938
+ self.assertEqual(attributes[0].event_uuid, second.uuid)
939
+ # include_event_tags
940
+ attributes = self.user_misp_connector.search(controller='attributes', eventid=second.id, include_event_tags=True)
941
+ self.assertEqual(attributes[0].tags[0].name, 'tlp:amber___test')
942
+
943
+ # event_timestamp
944
+ time.sleep(1)
945
+ second.add_attribute('ip-src', '8.8.8.9')
946
+ second = self.user_misp_connector.update_event(second)
947
+ events = self.user_misp_connector.search(event_timestamp=second.timestamp.timestamp())
948
+ self.assertEqual(len(events), 1)
949
+
950
+ # searchall
951
+ second.add_attribute('text', 'This is a test for the full text search', comment='Test stuff comment')
952
+ second = self.user_misp_connector.update_event(second)
953
+ events = self.user_misp_connector.search(value='%for the full text%', searchall=True)
954
+ self.assertEqual(len(events), 1)
955
+
956
+ # warninglist
957
+ response = self.admin_misp_connector.toggle_warninglist(warninglist_name='%dns resolv%', force_enable=True) # enable ipv4 DNS.
958
+ self.assertDictEqual(response, {'saved': True, 'success': '3 warninglist(s) enabled'})
959
+ second.add_attribute('ip-src', '1.11.71.4')
960
+ second.add_attribute('ip-src', '9.9.9.9')
961
+ second = self.user_misp_connector.update_event(second)
962
+
963
+ events = self.user_misp_connector.search(eventid=second.id)
964
+ self.assertEqual(len(events), 1)
965
+ self.assertEqual(events[0].id, second.id)
966
+ self.assertEqual(len(events[0].attributes), 5)
967
+
968
+ events = self.user_misp_connector.search(eventid=second.id, enforce_warninglist=False)
969
+ self.assertEqual(len(events), 1)
970
+ self.assertEqual(events[0].id, second.id)
971
+ self.assertEqual(len(events[0].attributes), 5)
972
+
973
+ events = self.user_misp_connector.search(eventid=second.id, enforce_warninglist=True)
974
+ self.assertEqual(len(events), 1)
975
+ self.assertEqual(events[0].id, second.id)
976
+ self.assertEqual(len(events[0].attributes), 4)
977
+
978
+ # Test PyMISP.add_attribute with enforceWarninglist enabled
979
+ _e = events[0]
980
+ _a = _e.add_attribute('ip-src', '8.8.8.8', enforceWarninglist=True)
981
+ _a = self.user_misp_connector.add_attribute(_e, _a)
982
+ self.assertTrue('trips over a warninglist and enforceWarninglist is enforced' in _a['errors'][1]['errors'], _a)
983
+
984
+ response = self.admin_misp_connector.toggle_warninglist(warninglist_name='%dns resolv%') # disable ipv4 DNS.
985
+ self.assertDictEqual(response, {'saved': True, 'success': '3 warninglist(s) toggled'})
986
+
987
+ # Page / limit
988
+ attributes = self.user_misp_connector.search(controller='attributes', eventid=second.id, page=1, limit=3)
989
+ self.assertEqual(len(attributes), 3)
990
+
991
+ attributes = self.user_misp_connector.search(controller='attributes', eventid=second.id, page=2, limit=3)
992
+ self.assertEqual(len(attributes), 2)
993
+
994
+ time.sleep(1) # make sure the next attribute is added one at least one second later
995
+
996
+ # attachments
997
+ with open('tests/testlive_comprehensive.py', 'rb') as f:
998
+ first.add_attribute('malware-sample', value='testfile.py', data=BytesIO(f.read()))
999
+
1000
+ first = self.user_misp_connector.update_event(first)
1001
+ events = self.user_misp_connector.search(timestamp=first.timestamp.timestamp(), with_attachments=True,
1002
+ pythonify=True)
1003
+ self.assertEqual(len(events), 1)
1004
+ self.assertIs(type(events[0].attributes[-1].malware_binary), BytesIO)
1005
+ events = self.user_misp_connector.search(timestamp=first.timestamp.timestamp(), with_attachments=False,
1006
+ pythonify=True)
1007
+ self.assertEqual(len(events), 1)
1008
+ self.assertIs(events[0].attributes[-1].malware_binary, None)
1009
+
1010
+ # Search index
1011
+ # # Timestamp
1012
+ events = self.user_misp_connector.search_index(timestamp=first.timestamp.timestamp(),
1013
+ pythonify=True)
1014
+ self.assertEqual(len(events), 1)
1015
+ self.assertEqual(events[0].info, 'foo bar blah')
1016
+ self.assertEqual(events[0].attributes, [])
1017
+
1018
+ # # Info
1019
+ complex_info = r'C:\Windows\System32\notepad.exe'
1020
+ e = events[0]
1021
+ e.info = complex_info
1022
+ e = self.user_misp_connector.update_event(e, pythonify=True)
1023
+ # Issue: https://github.com/MISP/MISP/issues/6616
1024
+ complex_info_search = r'C:\\Windows\\System32\\notepad.exe'
1025
+ events = self.user_misp_connector.search_index(eventinfo=complex_info_search,
1026
+ pythonify=True)
1027
+ self.assertEqual(len(events), 1)
1028
+ self.assertEqual(events[0].info, complex_info)
1029
+ self.assertEqual(events[0].attributes, [])
1030
+
1031
+ # Contact reporter
1032
+ r = self.user_misp_connector.contact_event_reporter(events[0].id, 'This is a test')
1033
+ self.assertEqual(r['message'], 'Email sent to the reporter.')
1034
+ finally:
1035
+ # Delete event
1036
+ self.admin_misp_connector.delete_event(first)
1037
+ self.admin_misp_connector.delete_event(second)
1038
+
1039
+ def test_event_add_update_metadata(self) -> None:
1040
+ event = self.create_simple_event()
1041
+ event.add_attribute('ip-src', '9.9.9.9')
1042
+ try:
1043
+ response = self.user_misp_connector.add_event(event, metadata=True)
1044
+ self.assertEqual(len(response.attributes), 0) # response should contains zero attributes
1045
+
1046
+ event.info = "New name ©"
1047
+ response = self.user_misp_connector.update_event(event, metadata=True)
1048
+ self.assertEqual(response.info, event.info)
1049
+ self.assertEqual(len(response.attributes), 0) # response should contains zero attributes
1050
+ finally: # cleanup
1051
+ self.admin_misp_connector.delete_event(event)
1052
+
1053
+ def test_extend_event(self) -> None:
1054
+ first = self.create_simple_event()
1055
+ first.info = 'parent event'
1056
+ first.add_tag('tlp:amber___test')
1057
+ first.set_date('2018-09-01')
1058
+ second = self.create_simple_event()
1059
+ second.info = 'event extension'
1060
+ second.add_tag('tlp:amber___test')
1061
+ second.set_date('2018-09-01')
1062
+ second.add_attribute('ip-src', '9.9.9.9')
1063
+ try:
1064
+ first = self.user_misp_connector.add_event(first)
1065
+ second = self.user_misp_connector.add_event(second)
1066
+ first_extended = self.user_misp_connector.update_event({'extends_uuid': second.uuid}, event_id=first, pythonify=True)
1067
+ self.assertTrue(isinstance(first_extended, MISPEvent), first_extended)
1068
+ self.assertEqual(first_extended.extends_uuid, second.uuid)
1069
+ finally:
1070
+ # Delete event
1071
+ self.admin_misp_connector.delete_event(first)
1072
+ self.admin_misp_connector.delete_event(second)
1073
+
1074
+ def test_edit_attribute(self) -> None:
1075
+ first = self.create_simple_event()
1076
+ try:
1077
+ first.attributes[0].comment = 'This is the original comment'
1078
+ first = self.user_misp_connector.add_event(first)
1079
+ first.attributes[0].comment = 'This is the modified comment'
1080
+ attribute = self.user_misp_connector.update_attribute(first.attributes[0])
1081
+ self.assertTrue(isinstance(attribute, MISPAttribute), attribute)
1082
+ self.assertEqual(attribute.comment, 'This is the modified comment')
1083
+ attribute = self.user_misp_connector.update_attribute({'comment': 'This is the modified comment, again'}, attribute)
1084
+ self.assertTrue(isinstance(attribute, MISPAttribute), attribute)
1085
+ self.assertEqual(attribute.comment, 'This is the modified comment, again', attribute)
1086
+ attribute = self.user_misp_connector.update_attribute({'disable_correlation': True}, attribute)
1087
+ self.assertTrue(attribute.disable_correlation, attribute)
1088
+ attribute = self.user_misp_connector.update_attribute({'disable_correlation': False}, attribute)
1089
+ self.assertFalse(attribute.disable_correlation, attribute)
1090
+ finally:
1091
+ # Delete event
1092
+ self.admin_misp_connector.delete_event(first)
1093
+
1094
+ def test_sightings(self) -> None:
1095
+ first = self.create_simple_event()
1096
+ second = self.create_simple_event()
1097
+ try:
1098
+ first = self.user_misp_connector.add_event(first)
1099
+ second = self.user_misp_connector.add_event(second)
1100
+
1101
+ current_ts = int(time.time())
1102
+ time.sleep(5)
1103
+ r = self.user_misp_connector.add_sighting({'value': first.attributes[0].value})
1104
+ self.assertEqual(int(r.attribute_id), first.attributes[0].id)
1105
+
1106
+ s = MISPSighting()
1107
+ s.value = second.attributes[0].value
1108
+ s.source = 'Testcases'
1109
+ s.type = '1'
1110
+ r = self.user_misp_connector.add_sighting(s, second.attributes[0])
1111
+ self.assertEqual(r.source, 'Testcases')
1112
+
1113
+ s = self.user_misp_connector.search_sightings(publish_timestamp=current_ts, include_attribute=True,
1114
+ include_event_meta=True, pythonify=True)
1115
+ self.assertEqual(len(s), 2)
1116
+ self.assertEqual(s[0]['event'].id, first.id)
1117
+ self.assertEqual(s[0]['attribute'].id, first.attributes[0].id)
1118
+
1119
+ s = self.user_misp_connector.search_sightings(publish_timestamp=current_ts,
1120
+ source='Testcases',
1121
+ include_attribute=True,
1122
+ include_event_meta=True,
1123
+ pythonify=True)
1124
+ self.assertEqual(len(s), 1)
1125
+ self.assertEqual(s[0]['event'].id, second.id, s)
1126
+ self.assertEqual(s[0]['attribute'].id, second.attributes[0].id)
1127
+
1128
+ s = self.user_misp_connector.search_sightings(publish_timestamp=current_ts,
1129
+ type_sighting='1',
1130
+ include_attribute=True,
1131
+ include_event_meta=True,
1132
+ pythonify=True)
1133
+ self.assertEqual(len(s), 1)
1134
+ self.assertEqual(s[0]['event'].id, second.id)
1135
+ self.assertEqual(s[0]['attribute'].id, second.attributes[0].id)
1136
+
1137
+ s = self.user_misp_connector.search_sightings(context='event',
1138
+ context_id=first.id,
1139
+ pythonify=True)
1140
+ self.assertEqual(len(s), 1)
1141
+ self.assertEqual(s[0]['sighting'].event_id, str(first.id))
1142
+
1143
+ s = self.user_misp_connector.search_sightings(context='attribute',
1144
+ context_id=second.attributes[0].id,
1145
+ pythonify=True)
1146
+ self.assertEqual(len(s), 1)
1147
+ self.assertEqual(s[0]['sighting'].attribute_id, str(second.attributes[0].id))
1148
+
1149
+ # Get sightings from event/attribute / org
1150
+ s = self.user_misp_connector.sightings(first)
1151
+ self.assertTrue(isinstance(s, list))
1152
+ self.assertEqual(int(s[0].attribute_id), first.attributes[0].id)
1153
+
1154
+ self.admin_misp_connector.add_sighting(s, second.attributes[0])
1155
+ s = self.user_misp_connector.sightings(second.attributes[0])
1156
+ self.assertEqual(len(s), 2)
1157
+ s = self.user_misp_connector.sightings(second.attributes[0], self.test_org)
1158
+ self.assertEqual(len(s), 1)
1159
+ self.assertEqual(s[0].org_id, self.test_org.id)
1160
+ # Delete sighting
1161
+ r = self.user_misp_connector.delete_sighting(s[0])
1162
+ self.assertEqual(r['message'], 'Sighting successfully deleted.')
1163
+
1164
+ finally:
1165
+ # Delete event
1166
+ self.admin_misp_connector.delete_event(first)
1167
+ self.admin_misp_connector.delete_event(second)
1168
+
1169
+ def test_search_csv(self) -> None:
1170
+ first = self.create_simple_event()
1171
+ first.attributes[0].comment = 'This is the original comment'
1172
+ second = self.create_simple_event()
1173
+ second.info = 'foo blah'
1174
+ second.set_date('2018-09-01')
1175
+ second.add_attribute('ip-src', '8.8.8.8')
1176
+ try:
1177
+ first = self.user_misp_connector.add_event(first)
1178
+ second = self.user_misp_connector.add_event(second)
1179
+
1180
+ response = self.user_misp_connector.publish(first, alert=False)
1181
+ self.assertEqual(response['errors'][1]['message'], 'You do not have permission to use this functionality.')
1182
+
1183
+ # Default search, attribute with to_ids == True
1184
+ first.attributes[0].to_ids = True
1185
+ first = self.user_misp_connector.update_event(first)
1186
+ self.admin_misp_connector.publish(first, alert=False)
1187
+ time.sleep(5)
1188
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp())
1189
+ self.assertEqual(len(csv), 1)
1190
+ self.assertEqual(csv[0]['value'], first.attributes[0].value)
1191
+
1192
+ # eventid
1193
+ csv = self.user_misp_connector.search(return_format='csv', eventid=first.id)
1194
+ self.assertEqual(len(csv), 1)
1195
+ self.assertEqual(csv[0]['value'], first.attributes[0].value)
1196
+
1197
+ # category
1198
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp(), category='Other')
1199
+ self.assertEqual(len(csv), 1)
1200
+ self.assertEqual(csv[0]['value'], first.attributes[0].value)
1201
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp(), category='Person')
1202
+ self.assertEqual(len(csv), 0)
1203
+
1204
+ # type_attribute
1205
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp(), type_attribute='text')
1206
+ self.assertEqual(len(csv), 1)
1207
+ self.assertEqual(csv[0]['value'], first.attributes[0].value)
1208
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp(), type_attribute='ip-src')
1209
+ self.assertEqual(len(csv), 0)
1210
+
1211
+ # context
1212
+ csv = self.user_misp_connector.search(return_format='csv', publish_timestamp=first.timestamp.timestamp(), include_context=True)
1213
+ self.assertEqual(len(csv), 1)
1214
+ self.assertTrue('event_info' in csv[0])
1215
+
1216
+ # date_from date_to
1217
+ csv = self.user_misp_connector.search(return_format='csv', date_from=date.today().isoformat())
1218
+ self.assertEqual(len(csv), 1)
1219
+ self.assertEqual(csv[0]['value'], first.attributes[0].value)
1220
+ csv = self.user_misp_connector.search(return_format='csv', date_from='2018-09-01', date_to='2018-09-02')
1221
+ self.assertEqual(len(csv), 2)
1222
+
1223
+ # headerless
1224
+ csv = self.user_misp_connector.search(return_format='csv', date_from='2018-09-01', date_to='2018-09-02', headerless=True)
1225
+ # Expects 2 lines after removing the empty ones.
1226
+ self.assertEqual(len(csv.strip().split('\n')), 2)
1227
+
1228
+ # include_context
1229
+ csv = self.user_misp_connector.search(return_format='csv', date_from='2018-09-01', date_to='2018-09-02', include_context=True)
1230
+ event_context_keys = ['event_info', 'event_member_org', 'event_source_org', 'event_distribution', 'event_threat_level_id', 'event_analysis', 'event_date', 'event_tag', 'event_timestamp']
1231
+ for k in event_context_keys:
1232
+ self.assertTrue(k in csv[0])
1233
+
1234
+ # requested_attributes
1235
+ columns = ['value', 'event_id']
1236
+ csv = self.user_misp_connector.search(return_format='csv', date_from='2018-09-01',
1237
+ date_to='2018-09-02', requested_attributes=columns)
1238
+ self.assertEqual(len(csv[0].keys()), 2)
1239
+ for k in columns:
1240
+ self.assertTrue(k in csv[0])
1241
+
1242
+ finally:
1243
+ # Mostly solved -> https://github.com/MISP/MISP/issues/4886
1244
+ time.sleep(5)
1245
+ # Delete event
1246
+ self.admin_misp_connector.delete_event(first)
1247
+ self.admin_misp_connector.delete_event(second)
1248
+
1249
+ def test_search_text(self) -> None:
1250
+ first = self.create_simple_event()
1251
+ first.add_attribute('ip-src', '8.8.8.8')
1252
+ first.publish()
1253
+ try:
1254
+ first = self.user_misp_connector.add_event(first)
1255
+ self.admin_misp_connector.publish(first)
1256
+ time.sleep(5)
1257
+ text = self.user_misp_connector.search(return_format='text', eventid=first.id)
1258
+ self.assertEqual('8.8.8.8', text.strip())
1259
+ finally:
1260
+ # Delete event
1261
+ self.admin_misp_connector.delete_event(first)
1262
+
1263
+ def test_search_stix(self) -> None:
1264
+ first = self.create_simple_event()
1265
+ first.add_attribute('ip-src', '8.8.8.8')
1266
+ try:
1267
+ first = self.user_misp_connector.add_event(first)
1268
+ stix = self.user_misp_connector.search(return_format='stix', eventid=first.id)
1269
+ self.assertTrue(stix['related_packages']['related_packages'][0]['package']['incidents'][0]['related_indicators']['indicators'][0]['indicator']['observable']['object']['properties']['address_value']['value'], '8.8.8.8')
1270
+ stix2 = self.user_misp_connector.search(return_format='stix2', eventid=first.id)
1271
+ self.assertEqual(stix2['objects'][-1]['pattern'], "[network-traffic:src_ref.type = 'ipv4-addr' AND network-traffic:src_ref.value = '8.8.8.8']")
1272
+ stix_xml = self.user_misp_connector.search(return_format='stix-xml', eventid=first.id)
1273
+ self.assertTrue('<AddressObj:Address_Value condition="Equals">8.8.8.8</AddressObj:Address_Value>' in stix_xml)
1274
+ finally:
1275
+ # Delete event
1276
+ self.admin_misp_connector.delete_event(first)
1277
+
1278
+ def test_update_object(self) -> None:
1279
+ first = self.create_simple_event()
1280
+ ip_dom = MISPObject('domain-ip')
1281
+ ip_dom.add_attribute('domain', value='google.fr')
1282
+ ip_dom.add_attribute('ip', value='8.8.8.8')
1283
+ first.add_object(ip_dom)
1284
+ try:
1285
+ # Update with full event
1286
+ first = self.user_misp_connector.add_event(first)
1287
+ first.objects[0].attributes[0].to_ids = False
1288
+ first.objects[0].add_attribute('ip', value='8.9.9.8')
1289
+ first.objects[0].add_attribute('ip', '8.9.9.10')
1290
+ first = self.user_misp_connector.update_event(first)
1291
+ self.assertFalse(first.objects[0].attributes[0].to_ids)
1292
+ self.assertEqual(first.objects[0].attributes[2].value, '8.9.9.8')
1293
+ self.assertEqual(first.objects[0].attributes[3].value, '8.9.9.10')
1294
+ # Update object attribute with update_attribute
1295
+ attr = first.objects[0].attributes[1]
1296
+ attr.to_ids = False
1297
+ new_attr = self.user_misp_connector.update_attribute(attr)
1298
+ self.assertFalse(new_attr.to_ids)
1299
+ # Update object only
1300
+ misp_object = self.user_misp_connector.get_object(first.objects[0].id)
1301
+ misp_object.attributes[2].value = '8.9.9.9'
1302
+ misp_object.attributes[2].to_ids = False
1303
+ misp_object = self.user_misp_connector.update_object(misp_object)
1304
+ self.assertEqual(misp_object.attributes[2].value, '8.9.9.9')
1305
+ self.assertFalse(misp_object.attributes[2].to_ids)
1306
+ # Test with add_attributes
1307
+ second = self.create_simple_event()
1308
+ ip_dom = MISPObject('domain-ip')
1309
+ ip_dom.add_attribute('domain', value='google.fr', disable_correlation=True)
1310
+ ip_dom.add_attributes('ip', {'value': '10.8.8.8', 'to_ids': False}, '10.9.8.8')
1311
+ ip_dom.add_attributes('ip', '11.8.8.8', '11.9.8.8')
1312
+ second.add_object(ip_dom)
1313
+ second = self.user_misp_connector.add_event(second)
1314
+ self.assertEqual(len(second.objects[0].attributes), 5)
1315
+ self.assertTrue(second.objects[0].attributes[0].disable_correlation)
1316
+ self.assertFalse(second.objects[0].attributes[1].to_ids)
1317
+ self.assertTrue(second.objects[0].attributes[2].to_ids)
1318
+
1319
+ # Test generic Tag methods
1320
+ r = self.admin_misp_connector.tag(second, 'generic_tag_test')
1321
+ self.assertTrue('successfully' in r['message'].lower() and f'({second.id})' in r['message'], r['message'])
1322
+ second = self.user_misp_connector.get_event(second.id, pythonify=True)
1323
+ self.assertTrue('generic_tag_test' == second.tags[0].name)
1324
+ # # Test local tag, shouldn't update the timestamp
1325
+ old_ts = second.timestamp
1326
+ r = self.admin_misp_connector.tag(second, 'generic_tag_test_local', local=True)
1327
+ second = self.user_misp_connector.get_event(second.id, pythonify=True)
1328
+ self.assertEqual(old_ts, second.timestamp)
1329
+
1330
+ r = self.admin_misp_connector.untag(second, 'generic_tag_test')
1331
+ r = self.admin_misp_connector.untag(second, 'generic_tag_test_local')
1332
+ self.assertTrue(r['message'].endswith(f'successfully removed from Event({second.id}).'), r['message'])
1333
+ second = self.user_misp_connector.get_event(second.id, pythonify=True)
1334
+ self.assertFalse(second.tags)
1335
+ # NOTE: object tagging not supported yet
1336
+ # r = self.admin_misp_connector.tag(second.objects[0].uuid, 'generic_tag_test')
1337
+ # self.assertTrue(r['message'].endswith(f'successfully attached to Object({second.objects[0].id}).'), r['message'])
1338
+ # r = self.admin_misp_connector.untag(second.objects[0].uuid, 'generic_tag_test')
1339
+ # self.assertTrue(r['message'].endswith(f'successfully removed from Object({second.objects[0].id}).'), r['message'])
1340
+ r = self.admin_misp_connector.tag(second.objects[0].attributes[0].uuid, 'generic_tag_test')
1341
+ self.assertTrue('successfully' in r['message'].lower() and f'({second.objects[0].attributes[0].id})' in r['message'], r['message'])
1342
+ attr = self.user_misp_connector.get_attribute(second.objects[0].attributes[0].uuid, pythonify=True)
1343
+ self.assertTrue('generic_tag_test' == attr.tags[0].name)
1344
+ r = self.admin_misp_connector.untag(second.objects[0].attributes[0].uuid, 'generic_tag_test')
1345
+ self.assertTrue(r['message'].endswith(f'successfully removed from Attribute({second.objects[0].attributes[0].id}).'), r['message'])
1346
+ second = self.user_misp_connector.get_event(second.id, pythonify=True)
1347
+ for tag in second.objects[0].attributes[0].tags:
1348
+ self.assertFalse('generic_tag_test' == tag.name)
1349
+ attr = self.user_misp_connector.get_attribute(second.objects[0].attributes[0].uuid, pythonify=True)
1350
+ self.assertFalse(attr.tags)
1351
+
1352
+ # Delete tag to avoid polluting the db
1353
+ tags = self.admin_misp_connector.tags(pythonify=True)
1354
+ for t in tags:
1355
+ if t.name == 'generic_tag_test':
1356
+ response = self.admin_misp_connector.delete_tag(t)
1357
+ self.assertEqual(response['message'], 'Tag deleted.')
1358
+
1359
+ # Test soft delete object
1360
+ second.delete_object(ip_dom.uuid)
1361
+ self.assertTrue(second.objects[-1].deleted)
1362
+ second = self.user_misp_connector.update_event(second)
1363
+ self.assertFalse(second.objects)
1364
+ second = self.user_misp_connector.get_event(second, deleted=True)
1365
+ self.assertTrue(second.objects[-1].deleted)
1366
+
1367
+ # Test delete object
1368
+ r = self.user_misp_connector.delete_object(second.objects[0])
1369
+ self.assertEqual(r['message'], 'Object deleted', r)
1370
+ new_second = self.admin_misp_connector.get_event(second, deleted=[0, 1], pythonify=True)
1371
+ self.assertEqual(len(new_second.objects), 1)
1372
+ # Hard delete
1373
+ response = self.admin_misp_connector.delete_object(second.objects[0], hard=True)
1374
+ self.assertEqual(response['message'], 'Object deleted')
1375
+ new_second = self.admin_misp_connector.get_event(second, deleted=[0, 1], pythonify=True)
1376
+ self.assertEqual(len(new_second.objects), 0)
1377
+ finally:
1378
+ # Delete event
1379
+ self.admin_misp_connector.delete_event(first)
1380
+ self.admin_misp_connector.delete_event(second)
1381
+
1382
+ def test_custom_template(self) -> None:
1383
+ first = self.create_simple_event()
1384
+ try:
1385
+ with open('tests/viper-test-files/test_files/whoami.exe', 'rb') as f:
1386
+ first.add_attribute('malware-sample', value='whoami.exe', data=BytesIO(f.read()), expand='binary')
1387
+ first.run_expansions()
1388
+ first = self.admin_misp_connector.add_event(first, pythonify=True)
1389
+ self.assertEqual(len(first.objects), 7)
1390
+ file_object = first.get_objects_by_name('file')[0]
1391
+ file_object.force_misp_objects_path_custom('tests/mispevent_testfiles', 'overwrite_file')
1392
+ file_object.add_attribute('test_overwrite', 'blah')
1393
+ obj_json = self.admin_misp_connector.update_object(file_object)
1394
+ self.assertTrue('Object' in obj_json, obj_json)
1395
+ self.assertTrue('name' in obj_json['Object'], obj_json)
1396
+ obj = MISPObject(obj_json['Object']['name'])
1397
+ obj.from_dict(**obj_json)
1398
+ self.assertEqual(obj.get_attributes_by_relation('test_overwrite')[0].value, 'blah')
1399
+
1400
+ # FULL object add & update with custom template
1401
+ new_object = MISPObject('overwrite_file', misp_objects_path_custom='tests/mispevent_testfiles')
1402
+ new_object.add_attribute('test_overwrite', 'barbaz')
1403
+ new_object.add_attribute('filename', 'barbaz.exe')
1404
+ new_object = self.admin_misp_connector.add_object(first, new_object, pythonify=True)
1405
+ self.assertEqual(new_object.get_attributes_by_relation('test_overwrite')[0].value, 'barbaz', new_object)
1406
+
1407
+ new_object.force_misp_objects_path_custom('tests/mispevent_testfiles', 'overwrite_file')
1408
+ new_object.add_attribute('filename', 'foobar.exe')
1409
+ new_object = self.admin_misp_connector.update_object(new_object, pythonify=True)
1410
+ self.assertEqual(new_object.get_attributes_by_relation('filename')[1].value, 'foobar.exe', new_object)
1411
+
1412
+ # Get existing custom object, modify it, update on MISP
1413
+ existing_object = self.admin_misp_connector.get_object(new_object.uuid, pythonify=True)
1414
+ # existing_object.force_misp_objects_path_custom('tests/mispevent_testfiles', 'overwrite_file')
1415
+ # The existing_object is a overwrite_file object, unless we uncomment the line above, type= is required below.
1416
+ existing_object.add_attribute('pattern-in-file', value='foo', type='text')
1417
+ updated_existing_object = self.admin_misp_connector.update_object(existing_object, pythonify=True)
1418
+ self.assertEqual(updated_existing_object.get_attributes_by_relation('pattern-in-file')[0].value, 'foo', updated_existing_object)
1419
+
1420
+ finally:
1421
+ # Delete event
1422
+ self.admin_misp_connector.delete_event(first)
1423
+
1424
+ def test_unknown_template(self) -> None:
1425
+ first = self.create_simple_event()
1426
+ attributeAsDict = [{'MyCoolAttribute': {'value': 'critical thing', 'type': 'text'}},
1427
+ {'MyCoolerAttribute': {'value': 'even worse', 'type': 'text', 'disable_correlation': True}}]
1428
+ misp_object = GenericObjectGenerator('my-cool-template')
1429
+ misp_object.generate_attributes(attributeAsDict)
1430
+ misp_object.template_uuid = uuid4()
1431
+ misp_object.template_id = 1
1432
+ misp_object.description = 'bar'
1433
+ setattr(misp_object, 'meta-category', 'foo')
1434
+ first.add_object(misp_object)
1435
+ blah_object = MISPObject('BLAH_TEST')
1436
+ blah_object.template_uuid = uuid4()
1437
+ blah_object.template_id = 1
1438
+ blah_object.description = 'foo'
1439
+ setattr(blah_object, 'meta-category', 'bar')
1440
+ blah_object.add_reference(misp_object.uuid, "test relation")
1441
+ blah_object.add_attribute('transaction-number', value='foo', type="text", disable_correlation=True)
1442
+ first.add_object(blah_object)
1443
+ try:
1444
+ first = self.user_misp_connector.add_event(first)
1445
+ self.assertEqual(len(first.objects[0].attributes), 2, first.objects[0].attributes)
1446
+ self.assertFalse(first.objects[0].attributes[0].disable_correlation)
1447
+ self.assertTrue(first.objects[0].attributes[1].disable_correlation)
1448
+ self.assertTrue(first.objects[1].attributes[0].disable_correlation)
1449
+
1450
+ # test update on totally unknown template
1451
+ first.objects[1].add_attribute('my relation', value='foobar', type='text', disable_correlation=True)
1452
+ updated_custom = self.user_misp_connector.update_object(first.objects[1], pythonify=True)
1453
+ self.assertEqual(updated_custom.attributes[1].value, 'foobar', updated_custom)
1454
+ finally:
1455
+ # Delete event
1456
+ self.admin_misp_connector.delete_event(first)
1457
+
1458
+ def test_domain_ip_object(self) -> None:
1459
+ first = self.create_simple_event()
1460
+ try:
1461
+ dom_ip_obj = DomainIPObject({'ip': ['1.1.1.1', {'value': '2.2.2.2', 'to_ids': False}],
1462
+ 'first-seen': '20190101',
1463
+ 'last-seen': '2019-02-03',
1464
+ 'domain': 'circl.lu'})
1465
+ first.add_object(dom_ip_obj)
1466
+ first = self.user_misp_connector.add_event(first)
1467
+ self.assertEqual(len(first.objects[0].attributes), 5)
1468
+ finally:
1469
+ # Delete event
1470
+ self.admin_misp_connector.delete_event(first)
1471
+
1472
+ def test_asn_object(self) -> None:
1473
+ first = self.create_simple_event()
1474
+ try:
1475
+ dom_ip_obj = ASNObject({'asn': '12345',
1476
+ 'first-seen': '20190101',
1477
+ 'last-seen': '2019-02-03'})
1478
+ first.add_object(dom_ip_obj)
1479
+ first = self.user_misp_connector.add_event(first)
1480
+ self.assertEqual(len(first.objects[0].attributes), 3)
1481
+ finally:
1482
+ # Delete event
1483
+ self.admin_misp_connector.delete_event(first)
1484
+
1485
+ def test_object_template(self) -> None:
1486
+ r = self.admin_misp_connector.update_object_templates()
1487
+ self.assertEqual(type(r), list)
1488
+ object_templates = self.admin_misp_connector.object_templates(pythonify=True)
1489
+ self.assertTrue(isinstance(object_templates, list))
1490
+ for object_template in object_templates:
1491
+ if object_template.name == 'file':
1492
+ break
1493
+
1494
+ template = self.admin_misp_connector.get_object_template(object_template.uuid, pythonify=True)
1495
+ self.assertEqual(template.name, 'file')
1496
+
1497
+ raw_template = self.admin_misp_connector.get_raw_object_template('domain-ip')
1498
+ raw_template['uuid'] = '4'
1499
+ mo = MISPObject('domain-ip', misp_objects_template_custom=raw_template)
1500
+ mo.add_attribute('ip', '8.8.8.8')
1501
+ mo.add_attribute('domain', 'google.fr')
1502
+ self.assertEqual(mo.template_uuid, '4')
1503
+
1504
+ def test_tags(self) -> None:
1505
+ # Get list
1506
+ tags = self.admin_misp_connector.tags(pythonify=True)
1507
+ self.assertTrue(isinstance(tags, list))
1508
+ # Get tag
1509
+ for tag in tags:
1510
+ if not tag.hide_tag:
1511
+ break
1512
+ tag = self.admin_misp_connector.get_tag(tag, pythonify=True)
1513
+ self.assertTrue('name' in tag)
1514
+ # Enable by MISPTag
1515
+ tag = self.admin_misp_connector.disable_tag(tag, pythonify=True)
1516
+ self.assertTrue(tag.hide_tag)
1517
+ tag = self.admin_misp_connector.enable_tag(tag, pythonify=True)
1518
+ self.assertFalse(tag.hide_tag)
1519
+ # Add tag
1520
+ tag = MISPTag()
1521
+ tag.name = 'this is a test tag'
1522
+ new_tag = self.admin_misp_connector.add_tag(tag, pythonify=True)
1523
+ self.assertEqual(new_tag.name, tag.name)
1524
+ # Add non-exportable tag
1525
+ tag = MISPTag()
1526
+ tag.name = 'non-exportable tag'
1527
+ tag.exportable = False
1528
+ non_exportable_tag = self.admin_misp_connector.add_tag(tag, pythonify=True)
1529
+ self.assertFalse(non_exportable_tag.exportable)
1530
+ first = self.create_simple_event()
1531
+ first.attributes[0].add_tag('non-exportable tag')
1532
+ # Add tag restricted to an org
1533
+ tag = MISPTag()
1534
+ tag.name = f'restricted to org {self.test_org.id}'
1535
+ tag.org_id = self.test_org.id
1536
+ tag_org_restricted = self.admin_misp_connector.add_tag(tag, pythonify=True)
1537
+ self.assertEqual(tag_org_restricted.org_id, tag.org_id)
1538
+ # Add tag restricted to a user
1539
+ tag.name = f'restricted to user {self.test_usr.id}'
1540
+ tag.user_id = self.test_usr.id
1541
+ tag_user_restricted = self.admin_misp_connector.add_tag(tag, pythonify=True)
1542
+ self.assertEqual(tag_user_restricted.user_id, tag.user_id)
1543
+ try:
1544
+ first = self.user_misp_connector.add_event(first)
1545
+ self.assertFalse(first.attributes[0].tags)
1546
+ first = self.admin_misp_connector.get_event(first, pythonify=True)
1547
+ # Reference: https://github.com/MISP/MISP/issues/1394
1548
+ self.assertFalse(first.attributes[0].tags)
1549
+ # Reference: https://github.com/MISP/PyMISP/issues/483
1550
+ r = self.delegate_user_misp_connector.tag(first, tag_org_restricted)
1551
+ # FIXME: The error message changed and is unhelpful.
1552
+ # self.assertEqual(r['errors'][1]['message'], 'Invalid Tag. This tag can only be set by a fixed organisation.')
1553
+ self.assertEqual(r['errors'][1]['message'], 'Invalid Target.')
1554
+ r = self.user_misp_connector.tag(first, tag_org_restricted)
1555
+ self.assertTrue('successfully' in r['message'].lower() and f'({first.id})' in r['message'], r['message'])
1556
+ r = self.pub_misp_connector.tag(first.attributes[0], tag_user_restricted)
1557
+ self.assertIn('Invalid Tag. This tag can only be set by a fixed user.', r['errors'][1]['errors'])
1558
+ r = self.user_misp_connector.tag(first.attributes[0], tag_user_restricted)
1559
+ self.assertTrue('successfully' in r['message'].lower() and f'({first.attributes[0].id})' in r['message'], r['message'])
1560
+ first = self.user_misp_connector.get_event(first, pythonify=True)
1561
+ self.assertTrue(len(first.attributes[0].tags) == 1)
1562
+ # test delete tag on attribute edit
1563
+ deleted_tag = first.attributes[0].tags[0]
1564
+ first.attributes[0].tags[0].delete()
1565
+ attribute = self.user_misp_connector.update_attribute(first.attributes[0], pythonify=True)
1566
+ for tag in attribute.tags:
1567
+ self.assertTrue(tag.name != deleted_tag.name)
1568
+ finally:
1569
+ # Delete event
1570
+ self.admin_misp_connector.delete_event(first)
1571
+
1572
+ # Search tag
1573
+ # Partial search
1574
+ tags = self.admin_misp_connector.search_tags(f'{new_tag.name[:5]}%', pythonify=True)
1575
+ self.assertEqual(tags[0].name, 'this is a test tag')
1576
+ # No tags found
1577
+ tags = self.admin_misp_connector.search_tags('not a tag')
1578
+ self.assertFalse(tags)
1579
+
1580
+ # Update tag
1581
+ non_exportable_tag.name = 'non-exportable tag - edit'
1582
+ non_exportable_tag_edited = self.admin_misp_connector.update_tag(non_exportable_tag, pythonify=True)
1583
+ self.assertTrue(non_exportable_tag_edited.name == 'non-exportable tag - edit', non_exportable_tag_edited.to_json(indent=2))
1584
+
1585
+ # Delete tag
1586
+ response = self.admin_misp_connector.delete_tag(new_tag)
1587
+ self.assertEqual(response['message'], 'Tag deleted.')
1588
+ response = self.admin_misp_connector.delete_tag(non_exportable_tag)
1589
+ self.assertEqual(response['message'], 'Tag deleted.')
1590
+ response = self.admin_misp_connector.delete_tag(tag_org_restricted)
1591
+ response = self.admin_misp_connector.delete_tag(tag_user_restricted)
1592
+
1593
+ def test_add_event_with_attachment_object_controller(self) -> None:
1594
+ first = self.create_simple_event()
1595
+ try:
1596
+ first = self.user_misp_connector.add_event(first)
1597
+ fo, peo, seos = make_binary_objects('tests/viper-test-files/test_files/whoami.exe')
1598
+ for s in seos:
1599
+ r = self.user_misp_connector.add_object(first, s)
1600
+ self.assertEqual(r.name, 'pe-section', r)
1601
+
1602
+ r = self.user_misp_connector.add_object(first, peo, pythonify=True)
1603
+ self.assertEqual(r.name, 'pe', r)
1604
+ for ref in peo.ObjectReference:
1605
+ r = self.user_misp_connector.add_object_reference(ref)
1606
+ self.assertEqual(r.object_uuid, peo.uuid, r.to_json())
1607
+
1608
+ r = self.user_misp_connector.add_object(first, fo)
1609
+ obj_attrs = r.get_attributes_by_relation('ssdeep')
1610
+ self.assertEqual(len(obj_attrs), 1, obj_attrs)
1611
+ self.assertEqual(r.name, 'file', r)
1612
+
1613
+ # Test break_on_duplicate at object level
1614
+ fo_dup, peo_dup, _ = make_binary_objects('tests/viper-test-files/test_files/whoami.exe')
1615
+ r = self.user_misp_connector.add_object(first, peo_dup, break_on_duplicate=True)
1616
+ self.assertTrue("Duplicate object found" in r['errors'][1]['errors'], r)
1617
+
1618
+ # Test break on duplicate with breakOnDuplicate key in object
1619
+ fo_dup.breakOnDuplicate = True
1620
+ r = self.user_misp_connector.add_object(first, fo_dup)
1621
+ self.assertTrue("Duplicate object found" in r['errors'][1]['errors'], r)
1622
+
1623
+ # Test refs
1624
+ r = self.user_misp_connector.add_object_reference(fo.ObjectReference[0])
1625
+ self.assertEqual(r.object_uuid, fo.uuid, r.to_json())
1626
+ self.assertEqual(r.referenced_uuid, peo.uuid, r.to_json())
1627
+ r = self.user_misp_connector.delete_object_reference(r)
1628
+ self.assertEqual(r['message'], 'ObjectReference deleted')
1629
+ finally:
1630
+ # Delete event
1631
+ self.admin_misp_connector.delete_event(first)
1632
+
1633
+ def test_add_event_with_attachment_object_controller__hard(self) -> None:
1634
+ first = self.create_simple_event()
1635
+ try:
1636
+ first = self.user_misp_connector.add_event(first)
1637
+ fo, peo, seos = make_binary_objects('tests/viper-test-files/test_files/whoami.exe')
1638
+ for s in seos:
1639
+ r = self.user_misp_connector.add_object(first, s)
1640
+ self.assertEqual(r.name, 'pe-section', r)
1641
+
1642
+ r = self.user_misp_connector.add_object(first, peo, pythonify=True)
1643
+ self.assertEqual(r.name, 'pe', r)
1644
+ for ref in peo.ObjectReference:
1645
+ r = self.user_misp_connector.add_object_reference(ref)
1646
+ self.assertEqual(r.object_uuid, peo.uuid, r.to_json())
1647
+
1648
+ r = self.user_misp_connector.add_object(first, fo)
1649
+ obj_attrs = r.get_attributes_by_relation('ssdeep')
1650
+ self.assertEqual(len(obj_attrs), 1, obj_attrs)
1651
+ self.assertEqual(r.name, 'file', r)
1652
+
1653
+ # Test break_on_duplicate at object level
1654
+ fo_dup, peo_dup, _ = make_binary_objects('tests/viper-test-files/test_files/whoami.exe')
1655
+ r = self.user_misp_connector.add_object(first, peo_dup, break_on_duplicate=True)
1656
+ self.assertTrue("Duplicate object found" in r['errors'][1]['errors'], r)
1657
+
1658
+ # Test break on duplicate with breakOnDuplicate key in object
1659
+ fo_dup.breakOnDuplicate = True
1660
+ r = self.user_misp_connector.add_object(first, fo_dup)
1661
+ self.assertTrue("Duplicate object found" in r['errors'][1]['errors'], r)
1662
+
1663
+ # Test refs
1664
+ r = self.user_misp_connector.add_object_reference(fo.ObjectReference[0])
1665
+ self.assertEqual(r.object_uuid, fo.uuid, r.to_json())
1666
+ self.assertEqual(r.referenced_uuid, peo.uuid, r.to_json())
1667
+ r = self.user_misp_connector.delete_object_reference(r, hard=True)
1668
+ self.assertEqual(r['message'], 'ObjectReference deleted')
1669
+ # TODO: verify that the reference is not soft-deleted instead
1670
+ finally:
1671
+ # Delete event
1672
+ self.admin_misp_connector.delete_event(first)
1673
+
1674
+ def test_lief_and_sign(self) -> None:
1675
+ first = self.create_simple_event()
1676
+ try:
1677
+ first = self.user_misp_connector.add_event(first)
1678
+ fo, peo, seos = make_binary_objects('tests/viper-test-files/test_files/chromeinstall-8u31.exe')
1679
+ # Make sure VT imphash is the same as the one generated by lief
1680
+ vtimphash = '697c52d3bf08cccfd62da7bc503fdceb'
1681
+ imphash = peo.get_attributes_by_relation('imphash')[0]
1682
+ self.assertEqual(imphash.value, vtimphash)
1683
+ # Make sure VT authentihash is the same as the one generated by lief
1684
+ vtauthentihash = 'eb7be5a6f8ef4c2da5a183b4a3177153183e344038c56a00f5d88570a373d858'
1685
+ authentihash = peo.get_attributes_by_relation('authentihash')[0]
1686
+ self.assertEqual(authentihash.value, vtauthentihash)
1687
+
1688
+ # The following is a duplicate of examples/add_file_object.py
1689
+ if seos:
1690
+ for s in seos:
1691
+ self.user_misp_connector.add_object(first, s)
1692
+
1693
+ if peo:
1694
+ if hasattr(peo, 'certificates') and hasattr(peo, 'signers'):
1695
+ # special authenticode case for PE objects
1696
+ for c in peo.certificates:
1697
+ self.user_misp_connector.add_object(first, c, pythonify=True)
1698
+ for s in peo.signers:
1699
+ self.user_misp_connector.add_object(first, s, pythonify=True)
1700
+ del peo.certificates
1701
+ del peo.signers
1702
+ del peo.sections
1703
+ self.user_misp_connector.add_object(first, peo, pythonify=True)
1704
+ for ref in peo.ObjectReference:
1705
+ self.user_misp_connector.add_object_reference(ref)
1706
+
1707
+ if fo:
1708
+ self.user_misp_connector.add_object(first, fo, pythonify=True)
1709
+ for ref in fo.ObjectReference:
1710
+ self.user_misp_connector.add_object_reference(ref)
1711
+
1712
+ first = self.user_misp_connector.get_event(first, pythonify=True)
1713
+ self.assertEqual(len(first.objects), 10, first.objects)
1714
+ finally:
1715
+ # Delete event
1716
+ self.admin_misp_connector.delete_event(first)
1717
+
1718
+ def test_add_event_with_attachment(self) -> None:
1719
+ first_send = self.create_simple_event()
1720
+ try:
1721
+ first = self.user_misp_connector.add_event(first_send)
1722
+ self.assertTrue(isinstance(first, MISPEvent), first)
1723
+ file_obj, bin_obj, sections = make_binary_objects('tests/viper-test-files/test_files/whoami.exe', standalone=False)
1724
+ first.add_object(file_obj)
1725
+ first.add_object(bin_obj)
1726
+ for s in sections:
1727
+ first.add_object(s)
1728
+ self.assertEqual(len(first.objects[0].references), 1)
1729
+ self.assertEqual(first.objects[0].references[0].relationship_type, 'includes')
1730
+ first = self.user_misp_connector.update_event(first)
1731
+ self.assertEqual(len(first.objects[0].references), 1)
1732
+ self.assertEqual(first.objects[0].references[0].relationship_type, 'includes')
1733
+ finally:
1734
+ # Delete event
1735
+ self.admin_misp_connector.delete_event(first)
1736
+
1737
+ def test_taxonomies(self) -> None:
1738
+ # Make sure we're up-to-date
1739
+ r = self.admin_misp_connector.update_taxonomies()
1740
+ self.assertEqual(r['name'], 'All taxonomy libraries are up to date already.')
1741
+ # Get list
1742
+ taxonomies = self.admin_misp_connector.taxonomies(pythonify=True)
1743
+ self.assertTrue(isinstance(taxonomies, list))
1744
+
1745
+ # Test fetching taxonomy by ID
1746
+ list_name_test = 'tlp'
1747
+ for tax in taxonomies:
1748
+ if tax.namespace == list_name_test:
1749
+ break
1750
+ r = self.admin_misp_connector.get_taxonomy(tax, pythonify=True)
1751
+ self.assertEqual(r.namespace, list_name_test)
1752
+ self.assertTrue('enabled' in r)
1753
+
1754
+ # Test fetching taxonomy by namespace
1755
+ r = self.admin_misp_connector.get_taxonomy("tlp", pythonify=True)
1756
+ self.assertEqual(r.namespace, "tlp")
1757
+
1758
+ r = self.admin_misp_connector.enable_taxonomy(tax)
1759
+ self.assertEqual(r['message'], 'Taxonomy enabled')
1760
+
1761
+ r = self.admin_misp_connector.enable_taxonomy_tags(tax)
1762
+ self.assertEqual(r['name'], 'The tag(s) has been saved.')
1763
+
1764
+ r = self.admin_misp_connector.disable_taxonomy(tax)
1765
+ self.assertEqual(r['message'], 'Taxonomy disabled')
1766
+
1767
+ # Test toggling the required status
1768
+ r = self.admin_misp_connector.set_taxonomy_required(tax, not tax.required)
1769
+ self.assertEqual(r['message'], 'Taxonomy toggleRequireded')
1770
+
1771
+ updatedTax = self.admin_misp_connector.get_taxonomy(tax, pythonify=True)
1772
+ self.assertFalse(tax.required == updatedTax.required)
1773
+
1774
+ # Return back to default required status
1775
+ r = self.admin_misp_connector.set_taxonomy_required(tax, not tax.required)
1776
+
1777
+ def test_warninglists(self) -> None:
1778
+ # Make sure we're up-to-date
1779
+ r = self.admin_misp_connector.update_warninglists()
1780
+ self.assertTrue('name' in r, msg=r)
1781
+ try:
1782
+ self.assertEqual(r['name'], 'All warninglists are up to date already.', msg=r)
1783
+ except Exception:
1784
+ print(r)
1785
+ # Get list
1786
+ warninglists = self.admin_misp_connector.warninglists(pythonify=True)
1787
+ self.assertTrue(isinstance(warninglists, list))
1788
+ list_name_test = 'List of known hashes with common false-positives (based on Florian Roth input list)'
1789
+ for wl in warninglists:
1790
+ if wl.name == list_name_test:
1791
+ break
1792
+ testwl = wl
1793
+ r = self.admin_misp_connector.get_warninglist(testwl, pythonify=True)
1794
+ self.assertEqual(r.name, list_name_test)
1795
+ self.assertTrue('WarninglistEntry' in r)
1796
+ r = self.admin_misp_connector.enable_warninglist(testwl)
1797
+ self.assertEqual(r['success'], '1 warninglist(s) enabled')
1798
+ # Check if a value is in a warning list
1799
+ md5_empty_file = 'd41d8cd98f00b204e9800998ecf8427e'
1800
+ r = self.user_misp_connector.values_in_warninglist([md5_empty_file])
1801
+ self.assertEqual(r[md5_empty_file][0]['name'], list_name_test)
1802
+
1803
+ r = self.admin_misp_connector.disable_warninglist(testwl)
1804
+ self.assertEqual(r['success'], '1 warninglist(s) disabled')
1805
+
1806
+ def test_noticelists(self) -> None:
1807
+ # Make sure we're up-to-date
1808
+ r = self.admin_misp_connector.update_noticelists()
1809
+ self.assertEqual(r['name'], 'All noticelists are up to date already.')
1810
+ # Get list
1811
+ noticelists = self.admin_misp_connector.noticelists(pythonify=True)
1812
+ self.assertTrue(isinstance(noticelists, list))
1813
+ list_name_test = 'gdpr'
1814
+ for nl in noticelists:
1815
+ if nl.name == list_name_test:
1816
+ break
1817
+ testnl = nl
1818
+ r = self.admin_misp_connector.get_noticelist(testnl, pythonify=True)
1819
+ self.assertEqual(r.name, list_name_test)
1820
+ # FIXME: https://github.com/MISP/MISP/issues/4856
1821
+ self.assertTrue('NoticelistEntry' in r)
1822
+ r = self.admin_misp_connector.enable_noticelist(testnl)
1823
+ self.assertTrue(r['Noticelist']['enabled'], r)
1824
+ r = self.admin_misp_connector.disable_noticelist(testnl)
1825
+ self.assertFalse(r['Noticelist']['enabled'], r)
1826
+
1827
+ def test_correlation_exclusions(self) -> None:
1828
+ newce = MISPCorrelationExclusion()
1829
+ newce.value = "test-correlation-exclusion"
1830
+ r = self.admin_misp_connector.add_correlation_exclusion(newce, pythonify=True)
1831
+ self.assertEqual(r.value, newce.value)
1832
+ correlation_exclusions = self.admin_misp_connector.correlation_exclusions(pythonify=True)
1833
+ self.assertTrue(isinstance(correlation_exclusions, list))
1834
+ testce = correlation_exclusions[0]
1835
+ r = self.admin_misp_connector.get_correlation_exclusion(testce, pythonify=True)
1836
+ self.assertEqual(r.value, testce.value)
1837
+ r = self.admin_misp_connector.delete_correlation_exclusion(r)
1838
+ self.assertTrue(r['success'])
1839
+ r = self.admin_misp_connector.clean_correlation_exclusions()
1840
+ self.assertTrue(r['success'])
1841
+
1842
+ def test_galaxies(self) -> None:
1843
+ # Make sure we're up-to-date
1844
+ r = self.admin_misp_connector.update_galaxies()
1845
+ self.assertEqual(r['name'], 'Galaxies updated.')
1846
+ # Get list
1847
+ galaxies = self.admin_misp_connector.galaxies(pythonify=True)
1848
+ self.assertTrue(isinstance(galaxies, list))
1849
+ list_name_test = 'Mobile Attack - Attack Pattern'
1850
+ for galaxy in galaxies:
1851
+ if galaxy.name == list_name_test:
1852
+ break
1853
+ r = self.admin_misp_connector.get_galaxy(galaxy, pythonify=True)
1854
+ self.assertEqual(r.name, list_name_test)
1855
+ # FIXME: Fails due to https://github.com/MISP/MISP/issues/4855
1856
+ # self.assertTrue('GalaxyCluster' in r)
1857
+
1858
+ def test_zmq(self) -> None:
1859
+ first = self.create_simple_event()
1860
+ try:
1861
+ first = self.user_misp_connector.add_event(first)
1862
+ r = self.admin_misp_connector.push_event_to_ZMQ(first)
1863
+ self.assertEqual(r['message'], 'Event published to ZMQ')
1864
+ finally:
1865
+ # Delete event
1866
+ self.admin_misp_connector.delete_event(first)
1867
+
1868
+ def test_csv_loader(self) -> None:
1869
+ csv1 = CSVLoader(template_name='file', csv_path=Path('tests/csv_testfiles/valid_fieldnames.csv'))
1870
+ event = MISPEvent()
1871
+ event.info = 'Test event from CSV loader'
1872
+ for o in csv1.load():
1873
+ event.add_object(**o)
1874
+
1875
+ csv2 = CSVLoader(template_name='file', csv_path=Path('tests/csv_testfiles/invalid_fieldnames.csv'),
1876
+ fieldnames=['sha1', 'filename', 'size-in-bytes'], has_fieldnames=True)
1877
+ try:
1878
+ first = self.user_misp_connector.add_event(event)
1879
+ for o in csv2.load():
1880
+ new_object = self.user_misp_connector.add_object(first, o)
1881
+ self.assertEqual(len(new_object.attributes), 3)
1882
+ finally:
1883
+ # Delete event
1884
+ self.admin_misp_connector.delete_event(first)
1885
+
1886
+ def test_user(self) -> None:
1887
+ # Get list
1888
+ users = self.admin_misp_connector.users(pythonify=True)
1889
+ self.assertTrue(isinstance(users, list))
1890
+ users_email = 'testusr@user.local'
1891
+ for user in users:
1892
+ if user.email == users_email:
1893
+ break
1894
+ else:
1895
+ raise Exception('Unable to find that user')
1896
+ self.assertEqual(user.email, users_email)
1897
+ # get user
1898
+ user = self.user_misp_connector.get_user(pythonify=True)
1899
+ # self.assertEqual(user.authkey, self.test_usr.authkey)
1900
+ # Update user
1901
+ user.email = 'foo@bar.de'
1902
+ user = self.admin_misp_connector.update_user(user, pythonify=True)
1903
+ self.assertEqual(user.email, 'foo@bar.de')
1904
+ # get API key
1905
+ key = self.user_misp_connector.get_new_authkey()
1906
+ self.assertTrue(isinstance(key, str))
1907
+
1908
+ def test_organisation(self) -> None:
1909
+ # Get list
1910
+ orgs = self.admin_misp_connector.organisations(pythonify=True)
1911
+ self.assertTrue(isinstance(orgs, list))
1912
+ org_name = 'ORGNAME'
1913
+ for org in orgs:
1914
+ if org.name == org_name:
1915
+ break
1916
+ self.assertEqual(org.name, org_name)
1917
+ # Get org
1918
+ organisation = self.user_misp_connector.get_organisation(self.test_usr.org_id)
1919
+ self.assertEqual(organisation.name, 'Test Org')
1920
+ # Update org
1921
+ organisation.name = 'blah'
1922
+ organisation = self.admin_misp_connector.update_organisation(organisation, pythonify=True)
1923
+ self.assertEqual(organisation.name, 'blah', organisation)
1924
+
1925
+ def test_org_search(self) -> None:
1926
+ orgs = self.admin_misp_connector.organisations(pythonify=True)
1927
+ org_name = 'ORGNAME'
1928
+ # Search by the org name
1929
+ orgs = self.admin_misp_connector.organisations(search=org_name, pythonify=True)
1930
+ # There should be one org returned
1931
+ self.assertTrue(len(orgs) == 1)
1932
+
1933
+ # This org should have the name ORGNAME
1934
+ self.assertEqual(orgs[0].name, org_name)
1935
+
1936
+ def test_user_search(self) -> None:
1937
+ users = self.admin_misp_connector.users(pythonify=True)
1938
+ emailAddr = users[0].email
1939
+
1940
+ users = self.admin_misp_connector.users(search=emailAddr)
1941
+ self.assertTrue(len(users) == 1)
1942
+ self.assertEqual(users[0]['User']['email'], emailAddr)
1943
+
1944
+ users = self.admin_misp_connector.users(
1945
+ search=emailAddr,
1946
+ organisation=users[0]['Organisation']['id'],
1947
+ pythonify=True
1948
+ )
1949
+ self.assertTrue(len(users) == 1)
1950
+ self.assertEqual(users[0].email, emailAddr)
1951
+
1952
+ def test_attribute(self) -> None:
1953
+ first = self.create_simple_event()
1954
+ second = self.create_simple_event()
1955
+ a = second.add_attribute('ip-src', '11.11.11.11')
1956
+ a.add_tag('testtag_admin_created')
1957
+ second.distribution = Distribution.all_communities
1958
+ try:
1959
+ first = self.user_misp_connector.add_event(first)
1960
+ second = self.admin_misp_connector.add_event(second, pythonify=True)
1961
+ # Get attribute
1962
+ attribute = self.user_misp_connector.get_attribute(first.attributes[0])
1963
+ self.assertEqual(first.attributes[0].uuid, attribute.uuid)
1964
+ # Add attribute
1965
+ new_attribute = MISPAttribute()
1966
+ new_attribute.value = '1.2.3.4'
1967
+ new_attribute.type = 'ip-dst'
1968
+ new_attribute = self.user_misp_connector.add_attribute(first, new_attribute)
1969
+ self.assertTrue(isinstance(new_attribute, MISPAttribute), new_attribute)
1970
+ self.assertEqual(new_attribute.value, '1.2.3.4', new_attribute)
1971
+ # Test attribute already in event
1972
+ # new_attribute.uuid = str(uuid4())
1973
+ # new_attribute = self.user_misp_connector.add_attribute(first, new_attribute)
1974
+ new_similar = MISPAttribute()
1975
+ new_similar.value = '1.2.3.4'
1976
+ new_similar.type = 'ip-dst'
1977
+ similar_error = self.user_misp_connector.add_attribute(first, new_similar)
1978
+ self.assertEqual(similar_error['errors'][1]['errors']['value'][0], 'A similar attribute already exists for this event.')
1979
+
1980
+ # Test add attribute break_on_duplicate=False
1981
+ time.sleep(5)
1982
+ new_similar = MISPAttribute()
1983
+ new_similar.value = '1.2.3.4'
1984
+ new_similar.type = 'ip-dst'
1985
+ new_similar = self.user_misp_connector.add_attribute(first, new_similar, break_on_duplicate=False)
1986
+ self.assertTrue(isinstance(new_similar, MISPAttribute), new_similar)
1987
+ self.assertGreater(new_similar.timestamp, new_attribute.timestamp)
1988
+
1989
+ # Test add multiple attributes at once
1990
+ attr0 = MISPAttribute()
1991
+ attr0.value = '0.0.0.0'
1992
+ attr0.type = 'ip-dst'
1993
+ response = self.user_misp_connector.add_attribute(first, [attr0])
1994
+ time.sleep(5)
1995
+ self.assertTrue(isinstance(response['attributes'], list), response['attributes'])
1996
+ self.assertEqual(response['attributes'][0].value, '0.0.0.0')
1997
+ attr1 = MISPAttribute()
1998
+ attr1.value = '1.2.3.4'
1999
+ attr1.type = 'ip-dst'
2000
+ attr2 = MISPAttribute()
2001
+ attr2.value = '1.2.3.5'
2002
+ attr2.type = 'ip-dst'
2003
+ attr3 = MISPAttribute()
2004
+ attr3.value = first.attributes[0].value
2005
+ attr3.type = first.attributes[0].type
2006
+ attr4 = MISPAttribute()
2007
+ attr4.value = '1.2.3.6'
2008
+ attr4.type = 'ip-dst'
2009
+ attr4.add_tag('tlp:amber___test_unique_not_created')
2010
+ attr4.add_tag('testtag_admin_created')
2011
+ response = self.user_misp_connector.add_attribute(first, [attr1, attr2, attr3, attr4])
2012
+ time.sleep(5)
2013
+ self.assertTrue(isinstance(response['attributes'], list), response['attributes'])
2014
+ self.assertEqual(response['attributes'][0].value, '1.2.3.5')
2015
+ self.assertEqual(response['attributes'][1].value, '1.2.3.6')
2016
+ self.assertTrue(isinstance(response['attributes'][1].tags, list), response['attributes'][1].to_json())
2017
+ self.assertTrue(len(response['attributes'][1].tags), response['attributes'][1].to_json())
2018
+ self.assertEqual(response['attributes'][1].tags[0].name, 'testtag_admin_created')
2019
+ self.assertEqual(response['errors']['attribute_0']['value'][0], 'A similar attribute already exists for this event.')
2020
+ self.assertEqual(response['errors']['attribute_2']['value'][0], 'A similar attribute already exists for this event.')
2021
+
2022
+ # Add attribute as proposal
2023
+ new_proposal = MISPAttribute()
2024
+ new_proposal.value = '5.2.3.4'
2025
+ new_proposal.type = 'ip-dst'
2026
+ new_proposal.category = 'Network activity'
2027
+ new_proposal = self.user_misp_connector.add_attribute_proposal(first.id, new_proposal)
2028
+ self.assertEqual(new_proposal.value, '5.2.3.4')
2029
+ # Update attribute
2030
+ new_attribute.value = '5.6.3.4'
2031
+ new_attribute = self.user_misp_connector.update_attribute(new_attribute)
2032
+ self.assertEqual(new_attribute.value, '5.6.3.4')
2033
+ # Update attribute as proposal
2034
+ new_proposal_update = self.user_misp_connector.update_attribute_proposal(new_attribute.id, {'to_ids': False})
2035
+ self.assertEqual(new_proposal_update.to_ids, False)
2036
+ # Delete attribute as proposal
2037
+ proposal_delete = self.user_misp_connector.delete_attribute_proposal(new_attribute)
2038
+ self.assertTrue(proposal_delete['saved'])
2039
+ # Get attribute proposal
2040
+ temp_new_proposal = self.user_misp_connector.get_attribute_proposal(new_proposal)
2041
+ self.assertEqual(temp_new_proposal.uuid, new_proposal.uuid)
2042
+ # Get attribute proposal*S*
2043
+ proposals = self.user_misp_connector.attribute_proposals()
2044
+ self.assertTrue(isinstance(proposals, list))
2045
+ self.assertEqual(len(proposals), 3)
2046
+ self.assertEqual(proposals[0].value, '5.2.3.4')
2047
+ # Get proposals on a specific event
2048
+ self.admin_misp_connector.add_attribute_proposal(second.id, {'type': 'ip-src', 'value': '123.123.123.1'})
2049
+ proposals = self.admin_misp_connector.attribute_proposals(pythonify=True)
2050
+ self.assertTrue(isinstance(proposals, list))
2051
+ self.assertEqual(len(proposals), 4)
2052
+ proposals = self.admin_misp_connector.attribute_proposals(second, pythonify=True)
2053
+ self.assertTrue(isinstance(proposals, list))
2054
+ self.assertEqual(len(proposals), 1)
2055
+ self.assertEqual(proposals[0].value, '123.123.123.1')
2056
+ # Accept attribute proposal - New attribute
2057
+ self.user_misp_connector.accept_attribute_proposal(new_proposal)
2058
+ first = self.user_misp_connector.get_event(first)
2059
+ self.assertEqual(first.attributes[-1].value, '5.2.3.4')
2060
+ # Accept attribute proposal - Attribute update
2061
+ response = self.user_misp_connector.accept_attribute_proposal(new_proposal_update)
2062
+ self.assertEqual(response['message'], 'Proposed change accepted.')
2063
+ attribute = self.user_misp_connector.get_attribute(new_attribute)
2064
+ self.assertEqual(attribute.to_ids, False)
2065
+ # Discard attribute proposal
2066
+ new_proposal_update = self.user_misp_connector.update_attribute_proposal(new_attribute.id, {'to_ids': True})
2067
+ response = self.user_misp_connector.discard_attribute_proposal(new_proposal_update)
2068
+ self.assertEqual(response['message'], 'Proposal discarded.')
2069
+ attribute = self.user_misp_connector.get_attribute(new_attribute)
2070
+ self.assertEqual(attribute.to_ids, False)
2071
+
2072
+ # Test fallback to proposal if the user doesn't own the event
2073
+ prop_attr = MISPAttribute()
2074
+ prop_attr.from_dict(**{'type': 'ip-dst', 'value': '123.43.32.21'})
2075
+ # Add attribute on event owned by someone else
2076
+ attribute = self.user_misp_connector.add_attribute(second.id, prop_attr)
2077
+ self.assertTrue(isinstance(attribute, MISPShadowAttribute), attribute)
2078
+ # Test if add proposal without category works - https://github.com/MISP/MISP/issues/4868
2079
+ attribute = self.user_misp_connector.add_attribute(second.id, {'type': 'ip-dst', 'value': '123.43.32.22'})
2080
+ self.assertTrue(isinstance(attribute, MISPShadowAttribute), attribute)
2081
+ # Add attribute with the same value as an existing proposal
2082
+ prop_attr.uuid = str(uuid4())
2083
+ attribute = self.admin_misp_connector.add_attribute(second, prop_attr, pythonify=True)
2084
+ prop_attr.uuid = str(uuid4())
2085
+ # Add a duplicate attribute (same value)
2086
+ attribute = self.admin_misp_connector.add_attribute(second, prop_attr, pythonify=True)
2087
+ self.assertTrue('errors' in attribute)
2088
+ # Update attribute owned by someone else
2089
+ attribute = self.user_misp_connector.update_attribute({'comment': 'blah'}, second.attributes[0].id)
2090
+ self.assertTrue(isinstance(attribute, MISPShadowAttribute), attribute)
2091
+ self.assertEqual(attribute.value, second.attributes[0].value)
2092
+ second = self.admin_misp_connector.get_event(second, pythonify=True)
2093
+ self.assertEqual(len(second.attributes), 3)
2094
+ # Delete attribute owned by someone else
2095
+ response = self.user_misp_connector.delete_attribute(second.attributes[1])
2096
+ self.assertTrue(response['success'])
2097
+ # Delete attribute owned by user
2098
+ response = self.admin_misp_connector.delete_attribute(second.attributes[1])
2099
+ self.assertEqual(response['message'], 'Attribute deleted.')
2100
+ # Hard delete
2101
+ response = self.admin_misp_connector.delete_attribute(second.attributes[0], hard=True)
2102
+ self.assertEqual(response['message'], 'Attribute deleted.')
2103
+ new_second = self.admin_misp_connector.get_event(second, deleted=[0, 1], pythonify=True)
2104
+ self.assertEqual(len(new_second.attributes), 2)
2105
+
2106
+ # Test attribute*S*
2107
+ attributes = self.admin_misp_connector.attributes()
2108
+ self.assertEqual(len(attributes), 7)
2109
+ # attributes = self.user_misp_connector.attributes()
2110
+ # self.assertEqual(len(attributes), 5)
2111
+ # Test event*S*
2112
+ events = self.admin_misp_connector.events()
2113
+ self.assertEqual(len(events), 2)
2114
+ events = self.user_misp_connector.events()
2115
+ self.assertEqual(len(events), 2)
2116
+ finally:
2117
+ # Delete event
2118
+ self.admin_misp_connector.delete_event(first)
2119
+ self.admin_misp_connector.delete_event(second)
2120
+
2121
+ def test_search_type_event_csv(self) -> None:
2122
+ try:
2123
+ first, second, third = self.environment()
2124
+ # Search as admin
2125
+ events = self.admin_misp_connector.search(return_format='csv', timestamp=first.timestamp.timestamp(), pythonify=True)
2126
+ self.assertTrue(isinstance(events, list))
2127
+ self.assertEqual(len(events), 8)
2128
+ attributes_types_search = self.admin_misp_connector.build_complex_query(or_parameters=['ip-src', 'ip-dst'])
2129
+ events = self.admin_misp_connector.search(return_format='csv', timestamp=first.timestamp.timestamp(),
2130
+ type_attribute=attributes_types_search, pythonify=True)
2131
+ self.assertTrue(isinstance(events, list))
2132
+ self.assertEqual(len(events), 6)
2133
+ finally:
2134
+ # Delete event
2135
+ self.admin_misp_connector.delete_event(first)
2136
+ self.admin_misp_connector.delete_event(second)
2137
+ self.admin_misp_connector.delete_event(third)
2138
+
2139
+ @unittest.skip("Not very important, skip for now.")
2140
+ def test_search_logs(self) -> None:
2141
+ r = self.admin_misp_connector.update_user({'email': 'testusr-changed@user.local'}, self.test_usr)
2142
+ r = self.admin_misp_connector.search_logs(model='User', created=date.today(), pythonify=True)
2143
+ for entry in r[-1:]:
2144
+ self.assertEqual(entry.action, 'edit')
2145
+ r = self.admin_misp_connector.search_logs(email='admin@admin.test', created=date.today(), pythonify=True)
2146
+ for entry in r[-1:]:
2147
+ self.assertEqual(entry.action, 'edit')
2148
+
2149
+ self.admin_misp_connector.update_user({'email': 'testusr@user.local'}, self.test_usr)
2150
+ time.sleep(5)
2151
+ r = self.admin_misp_connector.search_logs(model='User', limit=1, page=1, created=date.today(), pythonify=True)
2152
+ if r:
2153
+ last_change = r[0]
2154
+ self.assertEqual(last_change['change'], 'email (testusr-changed@user.local) => (testusr@user.local)', last_change)
2155
+ else:
2156
+ raise Exception('Unable to find log entry after updating the user')
2157
+
2158
+ def test_db_schema(self) -> None:
2159
+ diag = self.admin_misp_connector.db_schema_diagnostic()
2160
+ self.assertEqual(diag['actual_db_version'], diag['expected_db_version'], diag)
2161
+
2162
+ def test_live_acl(self) -> None:
2163
+ missing_acls = self.admin_misp_connector.remote_acl()
2164
+ self.assertEqual(missing_acls, [], msg=missing_acls)
2165
+
2166
+ def test_roles(self) -> None:
2167
+ role = self.admin_misp_connector.set_default_role(4)
2168
+ self.assertEqual(role['message'], 'Default role set.')
2169
+ self.admin_misp_connector.set_default_role(3)
2170
+ roles = self.admin_misp_connector.roles(pythonify=True)
2171
+ self.assertTrue(isinstance(roles, list))
2172
+ try:
2173
+ # Create a new role
2174
+ new_role = MISPRole()
2175
+ new_role.name = 'testrole'
2176
+ new_role = self.admin_misp_connector.add_role(new_role, pythonify=True)
2177
+ self.assertFalse(new_role.perm_sighting)
2178
+ new_role.perm_sighting = True
2179
+ new_role.max_execution_time = 1234
2180
+ updated_role = self.admin_misp_connector.update_role(new_role, pythonify=True)
2181
+ self.assertTrue(updated_role.perm_sighting)
2182
+ self.assertEqual(updated_role.max_execution_time, '1234')
2183
+ finally:
2184
+ self.admin_misp_connector.delete_role(new_role)
2185
+
2186
+ def test_describe_types(self) -> None:
2187
+ remote = self.admin_misp_connector.describe_types_remote
2188
+ remote_types = remote.pop('types')
2189
+ remote_categories = remote.pop('categories')
2190
+ remote_category_type_mappings = remote.pop('category_type_mappings')
2191
+
2192
+ local = dict(self.admin_misp_connector.describe_types_local)
2193
+ local_types = local.pop('types')
2194
+ local_categories = local.pop('categories')
2195
+ local_category_type_mappings = local.pop('category_type_mappings')
2196
+
2197
+ self.assertDictEqual(remote, local)
2198
+ self.assertEqual(sorted(remote_types), sorted(local_types))
2199
+ self.assertEqual(sorted(remote_categories), sorted(local_categories))
2200
+ for category, mapping in remote_category_type_mappings.items():
2201
+ self.assertEqual(sorted(local_category_type_mappings[category]), sorted(mapping))
2202
+ for typ in mapping:
2203
+ self.assertIn(typ, remote_types)
2204
+
2205
+ @unittest.skip("Tested elsewhere.")
2206
+ def test_versions(self) -> None:
2207
+ self.assertEqual(self.user_misp_connector.version, self.user_misp_connector.pymisp_version_master)
2208
+ self.assertEqual(self.user_misp_connector.misp_instance_version['version'],
2209
+ self.user_misp_connector.misp_instance_version_master['version'])
2210
+
2211
+ def test_statistics(self) -> None:
2212
+ try:
2213
+ # Attributes
2214
+ first, second, third = self.environment()
2215
+ expected_attr_stats = {'ip-dst': '2', 'ip-src': '1', 'text': '5'}
2216
+ attr_stats = self.admin_misp_connector.attributes_statistics()
2217
+ self.assertDictEqual(attr_stats, expected_attr_stats)
2218
+ expected_attr_stats_percent = {'ip-dst': '25%', 'ip-src': '12.5%', 'text': '62.5%'}
2219
+ attr_stats = self.admin_misp_connector.attributes_statistics(percentage=True)
2220
+ self.assertDictEqual(attr_stats, expected_attr_stats_percent)
2221
+ expected_attr_stats_category_percent = {'Network activity': '37.5%', 'Other': '62.5%'}
2222
+ attr_stats = self.admin_misp_connector.attributes_statistics(context='category', percentage=True)
2223
+ self.assertDictEqual(attr_stats, expected_attr_stats_category_percent)
2224
+ # Tags
2225
+ to_test = {'tags': {'tlp:white___test': '1'}, 'taxonomies': []}
2226
+ tags_stats = self.admin_misp_connector.tags_statistics()
2227
+ self.assertDictEqual(tags_stats, to_test)
2228
+ to_test = {'tags': {'tlp:white___test': '100%'}, 'taxonomies': []}
2229
+ tags_stats = self.admin_misp_connector.tags_statistics(percentage=True, name_sort=True)
2230
+ self.assertDictEqual(tags_stats, to_test)
2231
+ # Users
2232
+ users_stats = self.admin_misp_connector.users_statistics(context='data')
2233
+ self.assertTrue('stats' in users_stats)
2234
+
2235
+ users_stats = self.admin_misp_connector.users_statistics(context='orgs')
2236
+ self.assertTrue('ORGNAME' in list(users_stats.keys()))
2237
+
2238
+ users_stats = self.admin_misp_connector.users_statistics(context='users')
2239
+ self.assertEqual(list(users_stats.keys()), ['user', 'org_local', 'org_external'])
2240
+
2241
+ users_stats = self.admin_misp_connector.users_statistics(context='tags')
2242
+ self.assertEqual(list(users_stats.keys()), ['flatData', 'treemap'])
2243
+
2244
+ users_stats = self.admin_misp_connector.users_statistics(context='attributehistogram')
2245
+ self.assertTrue(isinstance(users_stats, list), users_stats)
2246
+
2247
+ self.user_misp_connector.add_sighting({'value': first.attributes[0].value})
2248
+ users_stats = self.user_misp_connector.users_statistics(context='sightings')
2249
+ self.assertEqual(list(users_stats.keys()), ['toplist', 'eventids'])
2250
+
2251
+ # FIXME this one fails on travis.
2252
+ # users_stats = self.admin_misp_connector.users_statistics(context='galaxyMatrix')
2253
+ # self.assertTrue('matrix' in users_stats)
2254
+ finally:
2255
+ # Delete event
2256
+ self.admin_misp_connector.delete_event(first)
2257
+ self.admin_misp_connector.delete_event(second)
2258
+ self.admin_misp_connector.delete_event(third)
2259
+
2260
+ def test_direct(self) -> None:
2261
+ try:
2262
+ r = self.user_misp_connector.direct_call('events/add', data={'info': 'foo'})
2263
+ event = MISPEvent()
2264
+ event.from_dict(**r)
2265
+ r = self.user_misp_connector.direct_call(f'events/view/{event.id}')
2266
+ event_get = MISPEvent()
2267
+ event_get.from_dict(**r)
2268
+ self.assertDictEqual(event.to_dict(), event_get.to_dict())
2269
+ r = self.user_misp_connector.direct_call('events/restSearch', data={"returnFormat": "csv",
2270
+ "type": {"AND": ["campaign-name", "threat-actor"]},
2271
+ "category": "Attribution", "includeEventUuid": 1})
2272
+ self.assertTrue(r.startswith('uuid,event_id,category,type,value'))
2273
+
2274
+ finally:
2275
+ self.admin_misp_connector.delete_event(event)
2276
+
2277
+ def test_freetext(self) -> None:
2278
+ first = self.create_simple_event()
2279
+ try:
2280
+ self.admin_misp_connector.toggle_warninglist(warninglist_name='%dns resolv%', force_enable=True)
2281
+ first = self.user_misp_connector.add_event(first)
2282
+ # disable_background_processing => returns the parsed data, before insertion
2283
+ r = self.user_misp_connector.freetext(first, '1.1.1.1 foo@bar.de', adhereToWarninglists=False,
2284
+ distribution=2, returnMetaAttributes=False, pythonify=True,
2285
+ kw_params={'disable_background_processing': 1})
2286
+ self.assertTrue(isinstance(r, list))
2287
+ self.assertEqual(r[0].value, '1.1.1.1')
2288
+ r = self.user_misp_connector.freetext(first, '9.9.9.9 foo@bar.com', adhereToWarninglists='soft',
2289
+ distribution=2, returnMetaAttributes=False, pythonify=True,
2290
+ kw_params={'disable_background_processing': 1})
2291
+ self.assertTrue(isinstance(r, list))
2292
+ self.assertEqual(r[0].value, '9.9.9.9')
2293
+ event = self.user_misp_connector.get_event(first, pythonify=True)
2294
+ self.assertEqual(event.attributes[3].value, '9.9.9.9')
2295
+ self.assertFalse(event.attributes[3].to_ids)
2296
+ r_wl = self.user_misp_connector.freetext(first, '8.8.8.8 foo@bar.de', adhereToWarninglists=True,
2297
+ distribution=2, returnMetaAttributes=False,
2298
+ kw_params={'disable_background_processing': 0})
2299
+ self.assertEqual(r_wl[0].value, '8.8.8.8')
2300
+ event = self.user_misp_connector.get_event(first, pythonify=True)
2301
+ for attribute in event.attributes:
2302
+ self.assertFalse(attribute.value == '8.8.8.8')
2303
+ r = self.user_misp_connector.freetext(first, '1.1.1.1 foo@bar.de', adhereToWarninglists=True,
2304
+ distribution=2, returnMetaAttributes=True)
2305
+ self.assertTrue(isinstance(r, list))
2306
+ self.assertTrue(isinstance(r[0]['types'], dict))
2307
+ finally:
2308
+ # Mostly solved https://github.com/MISP/MISP/issues/4886
2309
+ time.sleep(10)
2310
+ # Delete event
2311
+ self.admin_misp_connector.delete_event(first)
2312
+
2313
+ def test_sharing_groups(self) -> None:
2314
+ # add
2315
+ sg = MISPSharingGroup()
2316
+ sg.name = 'Testcases SG'
2317
+ sg.releasability = 'Testing'
2318
+ sharing_group = self.admin_misp_connector.add_sharing_group(sg, pythonify=True)
2319
+ self.assertEqual(sharing_group.name, 'Testcases SG')
2320
+ self.assertEqual(sharing_group.releasability, 'Testing')
2321
+
2322
+ # Change releasability
2323
+ r = self.admin_misp_connector.update_sharing_group({"releasability": "Testing updated"}, sharing_group)
2324
+ self.assertEqual(r['SharingGroup']['releasability'], 'Testing updated')
2325
+ r = self.admin_misp_connector.update_sharing_group({"releasability": "Testing updated - 2"}, sharing_group, pythonify=True)
2326
+ self.assertEqual(r.releasability, 'Testing updated - 2')
2327
+ # Change name
2328
+ r.name = 'Testcases SG - new name'
2329
+ r = self.admin_misp_connector.update_sharing_group(r, pythonify=True)
2330
+ self.assertEqual(r.name, 'Testcases SG - new name')
2331
+
2332
+ # Test `sharing_group_exists` method
2333
+ self.assertTrue(self.admin_misp_connector.sharing_group_exists(sharing_group))
2334
+ self.assertTrue(self.admin_misp_connector.sharing_group_exists(sharing_group.id))
2335
+ self.assertTrue(self.admin_misp_connector.sharing_group_exists(sharing_group.uuid))
2336
+
2337
+ # add org
2338
+ r = self.admin_misp_connector.add_org_to_sharing_group(sharing_group,
2339
+ self.test_org, extend=True)
2340
+ self.assertEqual(r['name'], 'Organisation added to the sharing group.')
2341
+
2342
+ # delete org
2343
+ r = self.admin_misp_connector.remove_org_from_sharing_group(sharing_group,
2344
+ self.test_org)
2345
+ self.assertEqual(r['name'], 'Organisation removed from the sharing group.', r)
2346
+ # Get list
2347
+ sharing_groups = self.admin_misp_connector.sharing_groups(pythonify=True)
2348
+ self.assertTrue(isinstance(sharing_groups, list))
2349
+ self.assertEqual(sharing_groups[0].name, 'Testcases SG - new name')
2350
+
2351
+ # Use the SG
2352
+
2353
+ first = self.create_simple_event()
2354
+ o = first.add_object(name='file')
2355
+ o.add_attribute('filename', value='foo2.exe')
2356
+ second_object = MISPObject('file')
2357
+ second_object.add_attribute("tlsh", value='92a4b4a3d342a21fe1147474c19c9ab6a01717713a0248a2bb15affce77c1c14a79b93',
2358
+ category="Payload delivery", to_ids=True, distribution=4, sharing_group_id=sharing_group.id)
2359
+
2360
+ try:
2361
+ first = self.user_misp_connector.add_event(first)
2362
+ first = self.admin_misp_connector.change_sharing_group_on_entity(first, sharing_group.id, pythonify=True)
2363
+ self.assertEqual(first.SharingGroup['name'], 'Testcases SG - new name')
2364
+
2365
+ first_object = self.admin_misp_connector.change_sharing_group_on_entity(first.objects[0], sharing_group.id, pythonify=True)
2366
+ self.assertEqual(first_object.sharing_group_id, sharing_group.id)
2367
+ first_attribute = self.admin_misp_connector.change_sharing_group_on_entity(first.attributes[0], sharing_group.id, pythonify=True)
2368
+ self.assertEqual(first_attribute.distribution, 4)
2369
+ self.assertEqual(first_attribute.sharing_group_id, int(sharing_group.id))
2370
+ # manual create
2371
+ second_object = self.admin_misp_connector.add_object(first.id, second_object, pythonify=True)
2372
+ self.assertEqual(second_object.attributes[0].sharing_group_id, int(sharing_group.id))
2373
+ # manual update
2374
+ first_object.add_attribute("tlsh", value='92a4b4a3d342a21fe1147474c19c9ab6a01717713a0248a2bb15affce77c1c14a79b93',
2375
+ category="Payload delivery", to_ids=True, distribution=4, sharing_group_id=sharing_group.id)
2376
+ first_object = self.admin_misp_connector.update_object(first_object, pythonify=True)
2377
+ self.assertEqual(first_object.attributes[-1].sharing_group_id, int(sharing_group.id))
2378
+ finally:
2379
+ # Delete event
2380
+ self.admin_misp_connector.delete_event(first)
2381
+ # Delete sharing group
2382
+ r = self.admin_misp_connector.delete_sharing_group(sharing_group.id)
2383
+ self.assertEqual(r['message'], 'SharingGroup deleted')
2384
+
2385
+ self.assertFalse(self.admin_misp_connector.sharing_group_exists(sharing_group))
2386
+ self.assertFalse(self.admin_misp_connector.sharing_group_exists(sharing_group.id))
2387
+ self.assertFalse(self.admin_misp_connector.sharing_group_exists(sharing_group.uuid))
2388
+
2389
+ def test_sharing_group(self) -> None:
2390
+ # add
2391
+ sg = MISPSharingGroup()
2392
+ sg.name = 'Testcases SG'
2393
+ sg.releasability = 'Testing'
2394
+ sharing_group = self.admin_misp_connector.add_sharing_group(sg, pythonify=True)
2395
+ # Add the org to the sharing group
2396
+ self.admin_misp_connector.add_org_to_sharing_group(
2397
+ sharing_group,
2398
+ self.test_org, extend=True
2399
+ )
2400
+ try:
2401
+ # Get the sharing group once again
2402
+ sharing_group = self.admin_misp_connector.get_sharing_group(sharing_group, pythonify=True)
2403
+
2404
+ self.assertTrue(isinstance(sharing_group, MISPSharingGroup))
2405
+ self.assertEqual(sharing_group.name, 'Testcases SG')
2406
+
2407
+ # Check we have the org field present and the first org is our org
2408
+ self.assertTrue(isinstance(getattr(sharing_group, "sgorgs"), list))
2409
+ self.assertEqual(sharing_group.sgorgs[0].org_id, self.test_org.id)
2410
+ finally:
2411
+ self.admin_misp_connector.delete_sharing_group(sharing_group.id)
2412
+ self.assertFalse(self.admin_misp_connector.sharing_group_exists(sharing_group))
2413
+
2414
+ def test_sharing_group_search(self) -> None:
2415
+ # Add sharing group
2416
+ sg = MISPSharingGroup()
2417
+ sg.name = 'Testcases SG'
2418
+ sg.releasability = 'Testing'
2419
+ sharing_group = self.admin_misp_connector.add_sharing_group(sg, pythonify=True)
2420
+ # Add the org to the sharing group
2421
+ self.admin_misp_connector.add_org_to_sharing_group(
2422
+ sharing_group,
2423
+ self.test_org, extend=True
2424
+ )
2425
+ # Add event
2426
+ event = self.create_simple_event()
2427
+ event.distribution = Distribution.sharing_group
2428
+ event.sharing_group_id = sharing_group.id
2429
+ # Create two attributes, one specifically for the sharing group,
2430
+ # another which inherits the event's SG
2431
+ event.add_attribute('ip-dst', '8.8.8.8', distribution=4, sharing_group_id=sharing_group.id)
2432
+ event.add_attribute('ip-dst', '9.9.9.9')
2433
+ event = self.user_misp_connector.add_event(event)
2434
+ attribute_ids = {a.id for a in event.attributes}
2435
+ try:
2436
+ # Try to query for the event
2437
+ events = self.user_misp_connector.search(sharinggroup=sharing_group.id, controller="events")
2438
+ # There should be one event
2439
+ self.assertTrue(len(events) == 1)
2440
+ # This event should be the one we added
2441
+ self.assertEqual(events[0].id, event.id)
2442
+ # Make sure the search isn't just returning everything
2443
+ events = self.user_misp_connector.search(sharinggroup=99999, controller="events")
2444
+
2445
+ self.assertTrue(len(events) == 0)
2446
+
2447
+ # Try to query for the attributes
2448
+ attributes = self.user_misp_connector.search(sharinggroup=sharing_group.id, controller="attributes")
2449
+ searched_attribute_ids = {a.id for a in attributes}
2450
+ # There should be two attributes
2451
+ # The extra 1 is the random UUID now created in the event
2452
+ self.assertTrue(len(attributes) == 2 + 1)
2453
+ # We should not be missing any of the attributes
2454
+ self.assertFalse(attribute_ids.difference(searched_attribute_ids))
2455
+ finally:
2456
+ self.user_misp_connector.delete_event(event.id)
2457
+ self.admin_misp_connector.delete_sharing_group(sharing_group.id)
2458
+
2459
+ self.assertFalse(self.admin_misp_connector.sharing_group_exists(sharing_group))
2460
+
2461
+ def test_feeds(self) -> None:
2462
+ # Add
2463
+ feed = MISPFeed()
2464
+ feed.name = 'TestFeed'
2465
+ feed.provider = 'TestFeed - Provider'
2466
+ feed.url = 'http://example.com'
2467
+ feed = self.admin_misp_connector.add_feed(feed, pythonify=True)
2468
+ self.assertEqual(feed.name, 'TestFeed')
2469
+ self.assertEqual(feed.url, 'http://example.com')
2470
+ # Update
2471
+ feed.name = 'TestFeed - Update'
2472
+ feed = self.admin_misp_connector.update_feed(feed, pythonify=True)
2473
+ self.assertEqual(feed.name, 'TestFeed - Update')
2474
+ # Delete
2475
+ r = self.admin_misp_connector.delete_feed(feed)
2476
+ self.assertEqual(r['message'], 'Feed deleted.')
2477
+ # List
2478
+ feeds = self.admin_misp_connector.feeds(pythonify=True)
2479
+ self.assertTrue(isinstance(feeds, list))
2480
+ for feed in feeds:
2481
+ if feed.name == 'The Botvrij.eu Data':
2482
+ break
2483
+ # Get
2484
+ botvrij = self.admin_misp_connector.get_feed(feed, pythonify=True)
2485
+ self.assertEqual(botvrij.url, "https://www.botvrij.eu/data/feed-osint")
2486
+ # Enable
2487
+ # MISP OSINT
2488
+ feed = self.admin_misp_connector.enable_feed(feeds[0].id, pythonify=True)
2489
+ self.assertTrue(feed.enabled)
2490
+ feed = self.admin_misp_connector.enable_feed_cache(feeds[0].id, pythonify=True)
2491
+ self.assertTrue(feed.caching_enabled)
2492
+ # Botvrij.eu
2493
+ feed = self.admin_misp_connector.enable_feed(botvrij.id, pythonify=True)
2494
+ self.assertTrue(feed.enabled)
2495
+ feed = self.admin_misp_connector.enable_feed_cache(botvrij.id, pythonify=True)
2496
+ self.assertTrue(feed.caching_enabled)
2497
+ # Cache
2498
+ r = self.admin_misp_connector.cache_feed(botvrij)
2499
+ self.assertEqual(r['message'], 'Feed caching job initiated.')
2500
+ # Fetch
2501
+ # Cannot test that, it fetches all the events.
2502
+ # r = self.admin_misp_connector.fetch_feed(botvrij)
2503
+ # FIXME https://github.com/MISP/MISP/issues/4834#issuecomment-511889274
2504
+ # self.assertEqual(r['message'], 'Feed caching job initiated.')
2505
+
2506
+ # Cache all enabled feeds
2507
+ r = self.admin_misp_connector.cache_all_feeds()
2508
+ self.assertEqual(r['message'], 'Feed caching job initiated.')
2509
+ # Compare all enabled feeds
2510
+ r = self.admin_misp_connector.compare_feeds()
2511
+ # FIXME: https://github.com/MISP/MISP/issues/4834#issuecomment-511890466
2512
+ # self.assertEqual(r['message'], 'Feed caching job initiated.')
2513
+ # Disable both feeds
2514
+ feed = self.admin_misp_connector.disable_feed(feeds[0].id, pythonify=True)
2515
+ self.assertFalse(feed.enabled)
2516
+ feed = self.admin_misp_connector.disable_feed(botvrij.id, pythonify=True)
2517
+ self.assertFalse(feed.enabled)
2518
+ feed = self.admin_misp_connector.disable_feed_cache(feeds[0].id, pythonify=True)
2519
+ self.assertFalse(feed.enabled)
2520
+ feed = self.admin_misp_connector.disable_feed_cache(botvrij.id, pythonify=True)
2521
+ self.assertFalse(feed.enabled)
2522
+ # Test enable csv feed - https://github.com/MISP/PyMISP/issues/574
2523
+ feeds = self.admin_misp_connector.feeds(pythonify=True)
2524
+ for feed in feeds:
2525
+ if feed.name == 'blockrules of rules.emergingthreats.net':
2526
+ e_thread_csv_feed = feed
2527
+ break
2528
+ updated_feed = self.admin_misp_connector.enable_feed(e_thread_csv_feed, pythonify=True)
2529
+ self.assertTrue(updated_feed.enabled)
2530
+ self.assertEqual(updated_feed.settings, e_thread_csv_feed.settings)
2531
+
2532
+ updated_feed = self.admin_misp_connector.disable_feed(e_thread_csv_feed, pythonify=True)
2533
+ self.assertFalse(updated_feed.enabled)
2534
+ self.assertEqual(updated_feed.settings, e_thread_csv_feed.settings)
2535
+
2536
+ # Test partial update
2537
+ updated_feed = self.admin_misp_connector.enable_feed(e_thread_csv_feed.id, pythonify=True)
2538
+ self.assertTrue(updated_feed.enabled)
2539
+ self.assertEqual(updated_feed.settings, e_thread_csv_feed.settings)
2540
+ updated_feed = self.admin_misp_connector.disable_feed(e_thread_csv_feed.id, pythonify=True)
2541
+ self.assertFalse(updated_feed.enabled)
2542
+ self.assertEqual(updated_feed.settings, e_thread_csv_feed.settings)
2543
+
2544
+ def test_servers(self) -> None:
2545
+ # add
2546
+ server = MISPServer()
2547
+ server.name = 'Test Server'
2548
+ server.url = 'https://127.0.0.1'
2549
+ server.remote_org_id = 1
2550
+ server.authkey = key
2551
+ server = self.admin_misp_connector.add_server(server, pythonify=True)
2552
+ self.assertEqual(server.name, 'Test Server')
2553
+ # Update
2554
+ server.name = 'Updated name'
2555
+ server = self.admin_misp_connector.update_server(server, pythonify=True)
2556
+ self.assertEqual(server.name, 'Updated name')
2557
+ # List
2558
+ servers = self.admin_misp_connector.servers(pythonify=True)
2559
+ self.assertEqual(servers[0].name, 'Updated name')
2560
+ # Delete
2561
+ r = self.admin_misp_connector.delete_server(server)
2562
+ self.assertEqual(r['name'], 'Server deleted')
2563
+
2564
+ def test_roles_expanded(self) -> None:
2565
+ '''Test all possible things regarding roles
2566
+ 1. Use existing roles (ID in test VM):
2567
+ * Read only (6): Can only connect via API and see events visible by its organisation
2568
+ * User (3): Same as readonly + create event, tag (using existing tags), add sighting
2569
+ * Publisher (4): Same as User + publish (also on zmq and kafka), and delegate
2570
+ * Org Admin (2): Same as publisher + admin org, audit, create tags, templates, sharing groups
2571
+ * Sync user (5): Same as publisher + sync, create tag, sharing group
2572
+ * admin (1): Same as Org admin and sync user + site admin, edit regexes, edit object templates
2573
+ 2. Create roles:
2574
+ * No Auth key access
2575
+ * Auth key (=> Read only)
2576
+ * + tagger
2577
+ * + sightings creator (=> User)
2578
+ * +
2579
+ '''
2580
+ # Creates a test user for roles
2581
+ user = MISPUser()
2582
+ user.email = 'testusr-roles@user.local'
2583
+ user.org_id = self.test_org.id
2584
+ tag = MISPTag()
2585
+ tag.name = 'tlp:white___test'
2586
+ try:
2587
+ test_roles_user = self.admin_misp_connector.add_user(user, pythonify=True)
2588
+ test_tag = self.admin_misp_connector.add_tag(tag, pythonify=True)
2589
+ test_roles_user_connector = PyMISP(url, test_roles_user.authkey, verifycert, debug=False)
2590
+ test_roles_user_connector.toggle_global_pythonify()
2591
+ # ===== Read Only
2592
+ self.admin_misp_connector.update_user({'role_id': 6}, test_roles_user)
2593
+ base_event = MISPEvent()
2594
+ base_event.info = 'Test Roles'
2595
+ base_event.distribution = 0
2596
+ base_event.add_attribute('ip-dst', '8.8.8.8')
2597
+ base_event.add_attribute('ip-dst', '9.9.9.9')
2598
+ base_event.attributes[0].add_tag('tlp:white___test')
2599
+ r = test_roles_user_connector.add_event(base_event)
2600
+ self.assertTrue(isinstance(r['errors'], tuple), r['errors'])
2601
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2602
+ try:
2603
+ e = self.user_misp_connector.add_event(base_event, pythonify=True)
2604
+ e = test_roles_user_connector.get_event(e)
2605
+ self.assertEqual(e.info, 'Test Roles')
2606
+ self.assertEqual(e.attributes[0].tags[0].name, 'tlp:white___test')
2607
+ r = test_roles_user_connector.publish(e)
2608
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2609
+ r = test_roles_user_connector.tag(e.attributes[1], 'tlp:white___test')
2610
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2611
+ r = test_roles_user_connector.add_sighting({'name': 'foo'}, e.attributes[1])
2612
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2613
+
2614
+ self.user_misp_connector.add_sighting({'source': 'blah'}, e.attributes[0])
2615
+ sightings = test_roles_user_connector.sightings(e.attributes[0])
2616
+ self.assertEqual(sightings[0].source, 'blah')
2617
+
2618
+ e = test_roles_user_connector.get_event(e)
2619
+ self.assertEqual(e.attributes[0].sightings[0].source, 'blah')
2620
+ # FIXME: http://github.com/MISP/MISP/issues/5022
2621
+ # a = test_roles_user_connector.get_attribute(e.attributes[0])
2622
+ # self.assertEqual(a.sightings[0].source, 'blah')
2623
+
2624
+ # ===== User (the capabilities were tested just before, only testing the publisher capabilities)
2625
+ self.admin_misp_connector.update_user({'role_id': 3}, test_roles_user)
2626
+ r = test_roles_user_connector.publish(e)
2627
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2628
+ r = test_roles_user_connector.delegate_event(e, self.test_org_delegate)
2629
+ self.assertEqual(r['errors'][1]['message'], 'You do not have permission to use this functionality.', r)
2630
+ # ===== Publisher
2631
+ # Make sure the delegation is enabled
2632
+ r = self.admin_misp_connector.set_server_setting('MISP.delegation', True, force=True)
2633
+ self.assertEqual(r['message'], 'Field updated', r)
2634
+ setting = self.admin_misp_connector.get_server_setting('MISP.delegation')
2635
+ self.assertTrue(setting['value'])
2636
+ # ======
2637
+ self.admin_misp_connector.update_user({'role_id': 4}, test_roles_user)
2638
+ r = test_roles_user_connector.publish(e)
2639
+ self.assertEqual(r['message'], 'Job queued', r)
2640
+ delegation = test_roles_user_connector.delegate_event(e, self.test_org_delegate)
2641
+ self.assertEqual(delegation.org_id, self.test_org_delegate.id)
2642
+ self.assertEqual(delegation.requester_org_id, self.test_org.id)
2643
+ r = test_roles_user_connector.accept_event_delegation(delegation.id)
2644
+ self.assertEqual(r['errors'][1]['message'], 'You are not authorised to do that.', r)
2645
+ # Test delegation
2646
+ delegations = self.delegate_user_misp_connector.event_delegations()
2647
+ self.assertEqual(delegations[0].id, delegation.id)
2648
+ r = self.delegate_user_misp_connector.accept_event_delegation(delegation)
2649
+ self.assertEqual(r['message'], 'Event ownership transferred.')
2650
+ e = self.delegate_user_misp_connector.get_event(e)
2651
+ self.assertTrue(isinstance(e, MISPEvent), e)
2652
+ self.assertEqual(e.info, 'Test Roles')
2653
+ self.assertEqual(e.org.name, 'Test Org - delegate')
2654
+ r = self.delegate_user_misp_connector.delete_event(e)
2655
+ self.assertEqual(r['message'], 'Event deleted.', r)
2656
+ # Change base_event UUID do we can add it
2657
+ base_event.uuid = str(uuid4())
2658
+ e = test_roles_user_connector.add_event(base_event)
2659
+ delegation = test_roles_user_connector.delegate_event(e, self.test_org_delegate)
2660
+ r = test_roles_user_connector.discard_event_delegation(delegation.id)
2661
+ self.assertEqual(r['message'], 'Delegation request deleted.')
2662
+
2663
+ e = test_roles_user_connector.get_event(e)
2664
+ self.assertTrue(isinstance(e, MISPEvent), e)
2665
+ self.assertEqual(e.info, 'Test Roles')
2666
+ self.assertEqual(e.org_id, int(self.test_org.id))
2667
+ finally:
2668
+ self.user_misp_connector.delete_event(e)
2669
+
2670
+ # Publisher
2671
+ self.admin_misp_connector.update_user({'role_id': 4}, test_roles_user)
2672
+ # Org Admin
2673
+ self.admin_misp_connector.update_user({'role_id': 2}, test_roles_user)
2674
+ # Sync User
2675
+ self.admin_misp_connector.update_user({'role_id': 5}, test_roles_user)
2676
+ # Admin
2677
+ self.admin_misp_connector.update_user({'role_id': 1}, test_roles_user)
2678
+ finally:
2679
+ self.admin_misp_connector.delete_user(test_roles_user)
2680
+ self.admin_misp_connector.delete_tag(test_tag)
2681
+
2682
+ def test_expansion(self) -> None:
2683
+ first = self.create_simple_event()
2684
+ try:
2685
+ md5_disk = hashlib.md5()
2686
+ with open('tests/viper-test-files/test_files/sample2.pe', 'rb') as f:
2687
+ filecontent = f.read()
2688
+ md5_disk.update(filecontent)
2689
+ malware_sample_initial_attribute = first.add_attribute('malware-sample', value='Big PE sample', data=BytesIO(filecontent), expand='binary')
2690
+ md5_init_attribute = hashlib.md5()
2691
+ md5_init_attribute.update(malware_sample_initial_attribute.malware_binary.getvalue())
2692
+ self.assertEqual(md5_init_attribute.digest(), md5_disk.digest())
2693
+
2694
+ first.run_expansions()
2695
+ first = self.admin_misp_connector.add_event(first, pythonify=True)
2696
+ self.assertEqual(len(first.objects), 8, first.objects)
2697
+ # Speed test
2698
+ # # reference time
2699
+ start = time.time()
2700
+ self.admin_misp_connector.get_event(first.id, pythonify=False)
2701
+ ref_time = time.time() - start
2702
+ # # Speed test pythonify
2703
+ start = time.time()
2704
+ first = self.admin_misp_connector.get_event(first.id, pythonify=True)
2705
+ pythonify_time = time.time() - start
2706
+ self.assertTrue((pythonify_time - ref_time) <= 0.5, f'Pythonify too slow: {ref_time} vs. {pythonify_time}.')
2707
+
2708
+ # Test on demand decrypt malware binary
2709
+ file_objects = first.get_objects_by_name('file')
2710
+ samples = file_objects[0].get_attributes_by_relation('malware-sample')
2711
+ binary = samples[0].malware_binary
2712
+ md5_from_server = hashlib.md5()
2713
+ md5_from_server.update(binary.getvalue())
2714
+ self.assertEqual(md5_from_server.digest(), md5_disk.digest())
2715
+ finally:
2716
+ # Delete event
2717
+ self.admin_misp_connector.delete_event(first)
2718
+
2719
+ def test_user_settings(self) -> None:
2720
+ first = self.create_simple_event()
2721
+ first.distribution = 3
2722
+ first.add_tag('test_publish_filter')
2723
+ first.add_tag('test_publish_filter_not')
2724
+ second = self.create_simple_event()
2725
+ second.distribution = 3
2726
+ try:
2727
+ # Set
2728
+ setting = self.admin_misp_connector.set_user_setting('dashboard_access', 1, pythonify=True)
2729
+ setting_value = {'Tag.name': 'test_publish_filter'}
2730
+ setting = self.admin_misp_connector.set_user_setting('publish_alert_filter', setting_value, pythonify=True)
2731
+ self.assertTrue(isinstance(setting, MISPUserSetting))
2732
+ self.assertEqual(setting.value, setting_value)
2733
+
2734
+ # Get
2735
+ # FIXME: https://github.com/MISP/MISP/issues/5297
2736
+ # setting = self.admin_misp_connector.get_user_setting('dashboard_access', pythonify=True)
2737
+
2738
+ # Get All
2739
+ user_settings = self.admin_misp_connector.user_settings(pythonify=True)
2740
+ # TODO: Make that one better
2741
+ self.assertTrue(isinstance(user_settings, list))
2742
+
2743
+ # Test if publish_alert_filter works
2744
+ # # Enable autoalert on admin
2745
+ self.admin_misp_connector._current_user.autoalert = True
2746
+ self.admin_misp_connector._current_user.termsaccepted = True
2747
+ admin_usr = self.admin_misp_connector.update_user(self.admin_misp_connector._current_user, pythonify=True)
2748
+ self.assertTrue(admin_usr.autoalert)
2749
+
2750
+ first = self.admin_misp_connector.add_event(first, pythonify=True)
2751
+ second = self.admin_misp_connector.add_event(second, pythonify=True)
2752
+ r = self.user_misp_connector.change_user_password('Password1234')
2753
+ self.assertEqual(r['message'], 'Password Changed.')
2754
+ self.test_usr.autoalert = True
2755
+ self.test_usr.termsaccepted = True
2756
+ user = self.user_misp_connector.update_user(self.test_usr, pythonify=True)
2757
+ self.assertTrue(user.autoalert)
2758
+ self.admin_misp_connector.publish(first, alert=True)
2759
+ self.admin_misp_connector.publish(second, alert=True)
2760
+ time.sleep(10)
2761
+ # FIXME https://github.com/MISP/MISP/issues/4872
2762
+ # mail_logs = self.admin_misp_connector.search_logs(model='User', action='email', limit=2, pythonify=True)
2763
+ mail_logs = self.admin_misp_connector.search_logs(model='User', action='email', created=datetime.now() - timedelta(seconds=30), pythonify=True)
2764
+ if mail_logs:
2765
+ # FIXME: On travis, the mails aren't working, so we stik that.
2766
+ self.assertEqual(len(mail_logs), 3)
2767
+ self.assertTrue(mail_logs[0].title.startswith(f'Email to {self.admin_misp_connector._current_user.email}'), mail_logs[0].title)
2768
+ self.assertTrue(mail_logs[1].title.startswith(f'Email to {self.user_misp_connector._current_user.email}'), mail_logs[1].title)
2769
+ self.assertTrue(mail_logs[2].title.startswith(f'Email to {self.user_misp_connector._current_user.email}'), mail_logs[2].title)
2770
+
2771
+ # Delete
2772
+ # FIXME: https://github.com/MISP/MISP/issues/5297
2773
+ # response = self.admin_misp_connector.delete_user_setting('publish_alert_filter')
2774
+ finally:
2775
+ self.test_usr.autoalert = False
2776
+ self.user_misp_connector.update_user(self.test_usr)
2777
+ # Delete event
2778
+ self.admin_misp_connector.delete_event(first)
2779
+ self.admin_misp_connector.delete_event(second)
2780
+
2781
+ def test_communities(self) -> None:
2782
+ communities = self.admin_misp_connector.communities(pythonify=True)
2783
+ self.assertEqual(communities[0].name, 'CIRCL Private Sector Information Sharing Community - aka MISPPRIV')
2784
+ community = self.admin_misp_connector.get_community(communities[1], pythonify=True)
2785
+ self.assertEqual(community.name, 'CIRCL n/g CSIRT information sharing community - aka MISP')
2786
+ # FIXME: Fails on travis for now due to GPG misconfigured
2787
+ # r = self.admin_misp_connector.request_community_access(community, mock=False)
2788
+ # self.assertTrue(r['message'], 'Request sent.')
2789
+ # r = self.admin_misp_connector.request_community_access(community, mock=True)
2790
+ # mail = email.message_from_string(r['headers'] + '\n' + r['message'])
2791
+ # for k, v in mail.items():
2792
+ # if k == 'To':
2793
+ # self.assertEqual(v, 'info@circl.lu')
2794
+
2795
+ def test_upload_stix(self) -> None:
2796
+ # FIXME https://github.com/MISP/MISP/issues/4892
2797
+ try:
2798
+ r1 = self.user_misp_connector.upload_stix('tests/stix1.xml-utf8', version='1')
2799
+ event_stix_one = MISPEvent()
2800
+ event_stix_one.load(r1.json())
2801
+ # self.assertEqual(event_stix_one.attributes[0], '8.8.8.8')
2802
+ self.admin_misp_connector.delete_event(event_stix_one)
2803
+ bl = self.admin_misp_connector.delete_event_blocklist(event_stix_one.uuid)
2804
+ self.assertTrue(bl['success'])
2805
+
2806
+ r2 = self.user_misp_connector.upload_stix('tests/stix2.json', version='2')
2807
+ event_stix_two = MISPEvent()
2808
+ event_stix_two.load(r2.json())
2809
+ # FIXME: the response is buggy.
2810
+ # self.assertEqual(event_stix_two.attributes[0], '8.8.8.8')
2811
+ self.admin_misp_connector.delete_event(event_stix_two)
2812
+ bl = self.admin_misp_connector.delete_event_blocklist(event_stix_two.uuid)
2813
+ self.assertTrue(bl['success'])
2814
+ finally:
2815
+ try:
2816
+ self.admin_misp_connector.delete_event(event_stix_one)
2817
+ self.admin_misp_connector.delete_event_blocklist(event_stix_one.uuid)
2818
+ except Exception:
2819
+ pass
2820
+ try:
2821
+ self.admin_misp_connector.delete_event(event_stix_two)
2822
+ self.admin_misp_connector.delete_event_blocklist(event_stix_two.uuid)
2823
+ except Exception:
2824
+ pass
2825
+
2826
+ def test_toggle_global_pythonify(self) -> None:
2827
+ first = self.create_simple_event()
2828
+ second = self.create_simple_event()
2829
+ try:
2830
+ self.admin_misp_connector.toggle_global_pythonify()
2831
+ first = self.admin_misp_connector.add_event(first)
2832
+ self.assertTrue(isinstance(first, MISPEvent))
2833
+ self.admin_misp_connector.toggle_global_pythonify()
2834
+ second = self.admin_misp_connector.add_event(second)
2835
+ self.assertTrue(isinstance(second, dict))
2836
+ finally:
2837
+ # Delete event
2838
+ self.admin_misp_connector.delete_event(first)
2839
+ self.admin_misp_connector.delete_event(second)
2840
+
2841
+ def test_first_last_seen(self) -> None:
2842
+ event = MISPEvent()
2843
+ event.info = 'Test First Last seen'
2844
+ event.add_attribute('ip-dst', '8.8.8.8', first_seen='2020-01-03', last_seen='2020-01-04T12:30:34.323242+0800')
2845
+ obj = event.add_object(name='file', first_seen=1580147259.268763, last_seen=1580147300)
2846
+ attr = obj.add_attribute('filename', 'blah.exe', comment="blah")
2847
+ attr.first_seen = '2022-01-30'
2848
+ attr.last_seen = '2022-02-23'
2849
+ try:
2850
+ first = self.admin_misp_connector.add_event(event, pythonify=True)
2851
+ # Simple attribute
2852
+ self.assertEqual(first.attributes[0].first_seen, datetime(2020, 1, 3, 0, 0).astimezone())
2853
+ self.assertEqual(first.attributes[0].last_seen, datetime(2020, 1, 4, 4, 30, 34, 323242, tzinfo=timezone.utc))
2854
+
2855
+ # Object
2856
+ self.assertEqual(first.objects[0].attributes[0].value, 'blah.exe')
2857
+ self.assertEqual(first.objects[0].attributes[0].comment, 'blah')
2858
+ self.assertEqual(first.objects[0].first_seen, datetime(2020, 1, 27, 17, 47, 39, 268763, tzinfo=timezone.utc))
2859
+ self.assertEqual(first.objects[0].last_seen, datetime(2020, 1, 27, 17, 48, 20, tzinfo=timezone.utc))
2860
+
2861
+ # Object attribute
2862
+ self.assertEqual(first.objects[0].attributes[0].first_seen, datetime(2022, 1, 30, 0, 0).astimezone())
2863
+ self.assertEqual(first.objects[0].attributes[0].last_seen, datetime(2022, 2, 23, 0, 0).astimezone())
2864
+
2865
+ # Update values
2866
+ # Attribute in full event
2867
+ now = datetime.now().astimezone()
2868
+ first.attributes[0].last_seen = now
2869
+ first = self.admin_misp_connector.update_event(first, pythonify=True)
2870
+ self.assertEqual(first.attributes[0].last_seen, now)
2871
+ # Object only
2872
+ now = datetime.now().astimezone()
2873
+ obj = first.objects[0]
2874
+ obj.last_seen = now
2875
+ obj = self.admin_misp_connector.update_object(obj, pythonify=True)
2876
+ self.assertEqual(obj.last_seen, now)
2877
+ # Attribute in object only
2878
+ now = datetime.now().astimezone()
2879
+ attr = obj.attributes[0]
2880
+ attr.first_seen = '2020-01-04'
2881
+ attr.last_seen = now
2882
+ attr = self.admin_misp_connector.update_attribute(attr, pythonify=True)
2883
+ self.assertEqual(attr.last_seen, now)
2884
+
2885
+ finally:
2886
+ self.admin_misp_connector.delete_event(first)
2887
+
2888
+ def test_registrations(self) -> None:
2889
+ r = register_user(url, 'self_register@user.local', organisation=self.test_org,
2890
+ org_name=self.test_org.name, verify=verifycert)
2891
+ self.assertTrue(r['saved'])
2892
+
2893
+ r = register_user(url, 'discard@tesst.de', verify=verifycert)
2894
+ self.assertTrue(r['saved'])
2895
+
2896
+ registrations = self.admin_misp_connector.user_registrations(pythonify=True)
2897
+ self.assertTrue(len(registrations), 2)
2898
+ self.assertEqual(registrations[0].data['email'], 'self_register@user.local')
2899
+ self.assertEqual(registrations[0].data['org_name'], 'Test Org')
2900
+ self.assertEqual(registrations[1].data['email'], 'discard@tesst.de')
2901
+
2902
+ m = self.admin_misp_connector.accept_user_registration(registrations[0], unsafe_fallback=True)
2903
+ self.assertTrue(m['saved'])
2904
+
2905
+ # delete new user
2906
+ for user in self.admin_misp_connector.users(pythonify=True):
2907
+ if user.email == registrations[0].data['email']:
2908
+ self.admin_misp_connector.delete_user(user)
2909
+ break
2910
+
2911
+ # Expected: accept registration fails because the orgname is missing
2912
+ m = self.admin_misp_connector.accept_user_registration(registrations[1], unsafe_fallback=True)
2913
+ self.assertEqual(m['errors'][1]['message'], 'No organisation selected. Supply an Organisation ID')
2914
+
2915
+ m = self.admin_misp_connector.discard_user_registration(registrations[1].id)
2916
+ self.assertEqual(m['name'], '1 registration(s) discarded.')
2917
+
2918
+ def test_search_workflow(self) -> None:
2919
+ first = self.create_simple_event()
2920
+ first.add_attribute('domain', 'google.com')
2921
+ tag = MISPTag()
2922
+ tag.name = 'my_tag'
2923
+ try:
2924
+ # Note: attribute 0 doesn't matter
2925
+ # Attribute 1 = google.com, no tag
2926
+ # Init tag and event
2927
+ tag = self.admin_misp_connector.add_tag(tag, pythonify=True)
2928
+ self.assertEqual(tag.name, 'my_tag')
2929
+ first = self.user_misp_connector.add_event(first, pythonify=True)
2930
+ time.sleep(10)
2931
+ # Add tag to attribute 1, add attribute 2, update
2932
+ first.attributes[1].add_tag(tag)
2933
+ first.add_attribute('domain', 'google.fr')
2934
+ # Attribute 1 = google.com, tag
2935
+ # Attribute 2 = google.fr, no tag
2936
+ first = self.user_misp_connector.update_event(first, pythonify=True)
2937
+ self.assertEqual(first.attributes[1].tags[0].name, 'my_tag')
2938
+ self.assertEqual(first.attributes[2].tags, [])
2939
+ updated_attrs = self.user_misp_connector.search(controller='attributes', eventid=first.id, timestamp='5s', pythonify=True)
2940
+ # Get two attributes, 0 (google.com) has a tag, 1 (google.fr) doesn't
2941
+ self.assertEqual(len(updated_attrs), 2)
2942
+ self.assertEqual(updated_attrs[0].tags[0].name, 'my_tag')
2943
+ self.assertEqual(updated_attrs[1].value, 'google.fr')
2944
+ self.assertEqual(updated_attrs[1].tags, [])
2945
+ # Get the metadata only of the event
2946
+ first_meta_only = self.user_misp_connector.search(eventid=first.id, metadata=True, pythonify=True)
2947
+
2948
+ # Add tag to attribute 1 (google.fr)
2949
+ attr_to_update = updated_attrs[1]
2950
+ attr_to_update.add_tag(tag)
2951
+ # attr_to_update.pop('timestamp')
2952
+ # Add new attribute to event with metadata only
2953
+ first_meta_only[0].add_attribute('domain', 'google.lu')
2954
+ # Add tag to new attribute
2955
+ first_meta_only[0].attributes[0].add_tag('my_tag')
2956
+ # Re-add attribute 1 (google.fr), newly tagged
2957
+ first_meta_only[0].add_attribute(**attr_to_update)
2958
+ # When we push, all the attributes should be tagged
2959
+ first = self.user_misp_connector.update_event(first_meta_only[0], pythonify=True)
2960
+ self.assertEqual(first.attributes[1].tags[0].name, 'my_tag')
2961
+ self.assertEqual(first.attributes[2].tags[0].name, 'my_tag')
2962
+ self.assertEqual(first.attributes[3].tags[0].name, 'my_tag')
2963
+ finally:
2964
+ self.admin_misp_connector.delete_event(first)
2965
+ self.admin_misp_connector.delete_tag(tag)
2966
+
2967
+ def test_search_workflow_ts(self) -> None:
2968
+ first = self.create_simple_event()
2969
+ first.add_attribute('domain', 'google.com')
2970
+ tag = MISPTag()
2971
+ tag.name = 'my_tag'
2972
+ try:
2973
+ # Note: attribute 0 doesn't matter
2974
+ # Attribute 1 = google.com, no tag
2975
+ # Init tag and event
2976
+ tag = self.admin_misp_connector.add_tag(tag, pythonify=True)
2977
+ self.assertEqual(tag.name, 'my_tag')
2978
+ first = self.user_misp_connector.add_event(first, pythonify=True)
2979
+ time.sleep(10)
2980
+ # Add tag to attribute 1, add attribute 2, update
2981
+ first.attributes[1].add_tag(tag)
2982
+ first.add_attribute('domain', 'google.fr')
2983
+ # Attribute 1 = google.com, tag
2984
+ # Attribute 2 = google.fr, no tag
2985
+ first = self.user_misp_connector.update_event(first, pythonify=True)
2986
+ self.assertEqual(first.attributes[1].tags[0].name, 'my_tag')
2987
+ self.assertEqual(first.attributes[2].tags, [])
2988
+ updated_attrs = self.user_misp_connector.search(controller='attributes', eventid=first.id, timestamp=first.timestamp.timestamp(), pythonify=True)
2989
+ # Get two attributes, 0 (google.com) has a tag, 1 (google.fr) doesn't
2990
+ self.assertEqual(len(updated_attrs), 2)
2991
+ self.assertEqual(updated_attrs[0].tags[0].name, 'my_tag')
2992
+ self.assertEqual(updated_attrs[1].value, 'google.fr')
2993
+ self.assertEqual(updated_attrs[1].tags, [])
2994
+ # Get the metadata only of the event
2995
+ first_meta_only = self.user_misp_connector.search(eventid=first.id, metadata=True, pythonify=True)
2996
+
2997
+ # Add tag to attribute 1 (google.fr)
2998
+ attr_to_update = updated_attrs[1]
2999
+ attr_to_update.add_tag(tag)
3000
+ # attr_to_update.pop('timestamp')
3001
+ # Add new attribute to event with metadata only
3002
+ first_meta_only[0].add_attribute('domain', 'google.lu')
3003
+ # Add tag to new attribute
3004
+ first_meta_only[0].attributes[0].add_tag('my_tag')
3005
+ # Re-add attribute 1 (google.fr), newly tagged
3006
+ first_meta_only[0].add_attribute(**attr_to_update)
3007
+ # When we push, all the attributes should be tagged
3008
+ first = self.user_misp_connector.update_event(first_meta_only[0], pythonify=True)
3009
+ self.assertEqual(first.attributes[1].tags[0].name, 'my_tag')
3010
+ self.assertEqual(first.attributes[2].tags[0].name, 'my_tag')
3011
+ self.assertEqual(first.attributes[3].tags[0].name, 'my_tag')
3012
+ finally:
3013
+ self.admin_misp_connector.delete_event(first)
3014
+ self.admin_misp_connector.delete_tag(tag)
3015
+
3016
+ def test_blocklists(self) -> None:
3017
+ first = self.create_simple_event()
3018
+ second = self.create_simple_event()
3019
+ second.Orgc = self.test_org
3020
+ to_delete: dict[str, MISPOrganisationBlocklist | MISPEventBlocklist] = {'bl_events': [], 'bl_organisations': []}
3021
+ try:
3022
+ # test events BL
3023
+ ebl: MISPEventBlocklist = self.admin_misp_connector.add_event_blocklist(uuids=[first.uuid])
3024
+ self.assertEqual(ebl['result']['successes'][0], first.uuid, ebl)
3025
+ bl_events = self.admin_misp_connector.event_blocklists(pythonify=True)
3026
+ for ble in bl_events:
3027
+ if ble.event_uuid == first.uuid:
3028
+ to_delete['bl_events'].append(ble)
3029
+ break
3030
+ else:
3031
+ raise Exception('Unable to find UUID in Events blocklist')
3032
+ first = self.user_misp_connector.add_event(first, pythonify=True)
3033
+ self.assertEqual(first['errors'][1]['message'], 'Event blocked by event blocklist.', first)
3034
+ ble.comment = 'This is a test'
3035
+ ble.event_info = 'foo'
3036
+ ble.event_orgc = 'bar'
3037
+ ble = self.admin_misp_connector.update_event_blocklist(ble, pythonify=True)
3038
+ self.assertEqual(ble.comment, 'This is a test')
3039
+ r = self.admin_misp_connector.delete_event_blocklist(ble)
3040
+ self.assertTrue(r['success'])
3041
+
3042
+ # test Org BL
3043
+ obl = self.admin_misp_connector.add_organisation_blocklist(uuids=self.test_org.uuid)
3044
+ self.assertEqual(obl['result']['successes'][0], self.test_org.uuid, obl)
3045
+ bl_orgs = self.admin_misp_connector.organisation_blocklists(pythonify=True)
3046
+ for blo in bl_orgs:
3047
+ if blo.org_uuid == self.test_org.uuid:
3048
+ to_delete['bl_organisations'].append(blo)
3049
+ break
3050
+ else:
3051
+ raise Exception('Unable to find UUID in Orgs blocklist')
3052
+ first = self.user_misp_connector.add_event(first, pythonify=True)
3053
+ self.assertEqual(first['errors'][1]['message'], 'Event blocked by organisation blocklist.', first)
3054
+
3055
+ blo.comment = 'This is a test'
3056
+ blo.org_name = 'bar'
3057
+ blo: MISPOrganisationBlocklist = self.admin_misp_connector.update_organisation_blocklist(blo, pythonify=True)
3058
+ self.assertEqual(blo.org_name, 'bar')
3059
+ r = self.admin_misp_connector.delete_organisation_blocklist(blo)
3060
+ self.assertTrue(r['success'])
3061
+
3062
+ finally:
3063
+ for ble in to_delete['bl_events']:
3064
+ self.admin_misp_connector.delete_event_blocklist(ble)
3065
+ for blo in to_delete['bl_organisations']:
3066
+ self.admin_misp_connector.delete_organisation_blocklist(blo)
3067
+
3068
+ def test_event_report(self) -> None:
3069
+ event = self.create_simple_event()
3070
+ new_event_report: MISPEventReport = MISPEventReport()
3071
+ new_event_report.name = "Test Event Report"
3072
+ new_event_report.content = "# Example report markdown"
3073
+ new_event_report.distribution = 5 # Inherit
3074
+ try:
3075
+ event = self.user_misp_connector.add_event(event)
3076
+ new_event_report = self.user_misp_connector.add_event_report(event.id, new_event_report) # type: ignore[assignment]
3077
+ # The event report should be linked by Event ID
3078
+ self.assertEqual(event.id, new_event_report.event_id)
3079
+
3080
+ event = self.user_misp_connector.get_event(event)
3081
+ # The Event Report should be present on the event
3082
+ self.assertEqual(new_event_report.id, event.event_reports[0].id)
3083
+
3084
+ new_event_report.name = "Updated Event Report"
3085
+ new_event_report.content = "Updated content"
3086
+ new_event_report = self.user_misp_connector.update_event_report(new_event_report) # type: ignore[assignment]
3087
+ # The event report should be updatable
3088
+ self.assertTrue(new_event_report.name == "Updated Event Report")
3089
+ self.assertTrue(new_event_report.content == "Updated content")
3090
+
3091
+ event_reports: list[MISPEventReport] = self.user_misp_connector.get_event_reports(event.id) # type: ignore[assignment]
3092
+ # The event report should be requestable by the Event ID
3093
+ self.assertEqual(new_event_report.id, event_reports[0].id)
3094
+
3095
+ response = self.user_misp_connector.delete_event_report(new_event_report)
3096
+ # The event report should be soft-deletable
3097
+ self.assertTrue(response['success'])
3098
+ self.assertEqual(response['name'], f'Event Report {new_event_report.uuid} soft deleted')
3099
+
3100
+ response = self.user_misp_connector.delete_event_report(new_event_report, True)
3101
+ self.assertTrue(response['success'])
3102
+ finally:
3103
+ self.user_misp_connector.delete_event(event)
3104
+ self.user_misp_connector.delete_event_report(new_event_report)
3105
+
3106
+ def test_search_galaxy(self) -> None:
3107
+ galaxies: list[MISPGalaxy] = self.admin_misp_connector.galaxies(pythonify=True) # type: ignore[assignment]
3108
+ galaxy: MISPGalaxy = galaxies[0]
3109
+ ret = self.admin_misp_connector.search_galaxy(value=galaxy.name, pythonify=True)
3110
+ self.assertEqual(len(ret), 1)
3111
+
3112
+ def test_galaxy_cluster(self) -> None:
3113
+ galaxies: list[MISPGalaxy] = self.admin_misp_connector.galaxies(pythonify=True) # type: ignore[assignment]
3114
+ galaxy: MISPGalaxy = galaxies[0]
3115
+ new_galaxy_cluster: MISPGalaxyCluster = MISPGalaxyCluster()
3116
+ new_galaxy_cluster.value = "Test Cluster"
3117
+ new_galaxy_cluster.authors = ["MISP"]
3118
+ new_galaxy_cluster.distribution = 1
3119
+ new_galaxy_cluster.description = "Example test cluster"
3120
+ try:
3121
+ if gid := galaxy.id:
3122
+ galaxy = self.admin_misp_connector.get_galaxy(gid, withCluster=True, pythonify=True) # type: ignore[assignment]
3123
+ else:
3124
+ raise Exception("No galaxy found")
3125
+ existing_galaxy_cluster = galaxy.clusters[0]
3126
+
3127
+ if gid := galaxy.id:
3128
+ new_galaxy_cluster = self.admin_misp_connector.add_galaxy_cluster(gid, new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3129
+ else:
3130
+ raise Exception("No galaxy found")
3131
+ # The new galaxy cluster should be under the selected galaxy
3132
+ self.assertEqual(galaxy.id, new_galaxy_cluster.galaxy_id)
3133
+ # The cluster should have the right value
3134
+ self.assertEqual(new_galaxy_cluster.value, "Test Cluster")
3135
+
3136
+ new_galaxy_cluster.add_cluster_element("synonyms", "Test2")
3137
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3138
+
3139
+ # The cluster should have one element that is a synonym
3140
+ self.assertEqual(len(new_galaxy_cluster.cluster_elements), 1)
3141
+ element = new_galaxy_cluster.cluster_elements[0]
3142
+ self.assertEqual(element.key, "synonyms")
3143
+ self.assertEqual(element.value, "Test2")
3144
+
3145
+ # The cluster should have the old meta as a prop
3146
+ self.assertEqual(new_galaxy_cluster.elements_meta, {'synonyms': ['Test2']})
3147
+
3148
+ # The cluster element should be updatable
3149
+ element.value = "Test3"
3150
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3151
+ element = new_galaxy_cluster.cluster_elements[0]
3152
+ self.assertEqual(element.value, "Test3")
3153
+
3154
+ new_galaxy_cluster.add_cluster_element("synonyms", "ToDelete")
3155
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3156
+ # The cluster should have two elements
3157
+ self.assertEqual(len(new_galaxy_cluster.cluster_elements), 2)
3158
+
3159
+ new_galaxy_cluster.cluster_elements = [e for e in new_galaxy_cluster.cluster_elements if e.value != "ToDelete"]
3160
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3161
+ # The cluster elements should be deletable
3162
+ self.assertEqual(len(new_galaxy_cluster.cluster_elements), 1)
3163
+
3164
+ new_galaxy_cluster.add_cluster_relation(existing_galaxy_cluster, "is-tested-by")
3165
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3166
+ # The cluster should have a relationship
3167
+ self.assertEqual(len(new_galaxy_cluster.cluster_relations), 1)
3168
+ relation = new_galaxy_cluster.cluster_relations[0]
3169
+ self.assertEqual(relation.referenced_galaxy_cluster_type, "is-tested-by")
3170
+ self.assertEqual(relation.referenced_galaxy_cluster_uuid, existing_galaxy_cluster.uuid)
3171
+
3172
+ relation.add_tag("tlp:amber")
3173
+ new_galaxy_cluster = self.admin_misp_connector.update_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3174
+ relation = new_galaxy_cluster.cluster_relations[0]
3175
+ # The relationship should have a tag of tlp:amber
3176
+ self.assertEqual(len(relation.tags), 1)
3177
+ self.assertEqual(relation.tags[0].name, "tlp:amber")
3178
+
3179
+ # The cluster relations should be deletable
3180
+ resp = self.admin_misp_connector.delete_galaxy_cluster_relation(relation)
3181
+ self.assertTrue(resp['success'])
3182
+ # The cluster relation should no longer be present
3183
+ new_galaxy_cluster = self.admin_misp_connector.get_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3184
+ self.assertEqual(len(new_galaxy_cluster.cluster_relations), 0)
3185
+
3186
+ resp = self.admin_misp_connector.delete_galaxy_cluster(new_galaxy_cluster)
3187
+ # Galaxy clusters should be soft deletable
3188
+ self.assertTrue(resp['success'])
3189
+ new_galaxy_cluster = self.admin_misp_connector.get_galaxy_cluster(new_galaxy_cluster, pythonify=True) # type: ignore[assignment]
3190
+ self.assertTrue(isinstance(new_galaxy_cluster, MISPGalaxyCluster))
3191
+
3192
+ resp = self.admin_misp_connector.delete_galaxy_cluster(new_galaxy_cluster, hard=True)
3193
+ # Galaxy clusters should be hard deletable
3194
+ self.assertTrue(resp['success'])
3195
+ resp = self.admin_misp_connector.get_galaxy_cluster(new_galaxy_cluster) # type: ignore[assignment]
3196
+ self.assertTrue("errors" in resp)
3197
+ finally:
3198
+ pass
3199
+
3200
+ def test_event_galaxy(self) -> None:
3201
+ event = self.create_simple_event()
3202
+ try:
3203
+ galaxies: list[MISPGalaxy] = self.admin_misp_connector.galaxies(pythonify=True) # type: ignore[assignment]
3204
+ galaxy: MISPGalaxy = galaxies[0]
3205
+ if gid := galaxy.id:
3206
+ galaxy = self.admin_misp_connector.get_galaxy(gid, withCluster=True, pythonify=True) # type: ignore[assignment]
3207
+ else:
3208
+ raise Exception("No galaxy found")
3209
+ galaxy_cluster: MISPGalaxyCluster = galaxy.clusters[0]
3210
+ event.add_tag(galaxy_cluster.tag_name)
3211
+ event = self.admin_misp_connector.add_event(event, pythonify=True)
3212
+ # The event should have a galaxy attached
3213
+ self.assertEqual(len(event.galaxies), 1)
3214
+ event_galaxy = event.galaxies[0]
3215
+ # The galaxy ID should equal the galaxy from which the cluster came from
3216
+ self.assertEqual(event_galaxy.id, galaxy.id)
3217
+ # The galaxy cluster should equal the cluster added
3218
+ self.assertEqual(event_galaxy.clusters[0].id, galaxy_cluster.id)
3219
+ finally:
3220
+ self.admin_misp_connector.delete_event(event)
3221
+
3222
+ def test_attach_galaxy_cluster(self) -> None:
3223
+ event = self.create_simple_event()
3224
+ event = self.admin_misp_connector.add_event(event, pythonify=True)
3225
+ try:
3226
+ galaxies: list[MISPGalaxy] = self.admin_misp_connector.galaxies(pythonify=True)
3227
+ galaxy: MISPGalaxy = galaxies[0]
3228
+ if gid := galaxy.id:
3229
+ galaxy = self.admin_misp_connector.get_galaxy(gid, withCluster=True, pythonify=True)
3230
+ else:
3231
+ raise Exception("No galaxy found")
3232
+ galaxy_cluster: MISPGalaxyCluster = galaxy.clusters[0]
3233
+ response = self.admin_misp_connector.attach_galaxy_cluster(event, galaxy_cluster)
3234
+ self.assertTrue(response['saved'])
3235
+ event = self.admin_misp_connector.get_event(event.id, pythonify=True)
3236
+
3237
+ self.assertEqual(len(event.galaxies), 1)
3238
+ event_galaxy = event.galaxies[0]
3239
+ # The galaxy ID should equal the galaxy from which the cluster came from
3240
+ self.assertEqual(event_galaxy.id, galaxy.id)
3241
+ # The galaxy cluster should equal the cluster added
3242
+ self.assertEqual(event_galaxy.clusters[0].id, galaxy_cluster.id)
3243
+
3244
+ galaxy_cluster: MISPGalaxyCluster = galaxy.clusters[1]
3245
+
3246
+ # Test on attribute
3247
+ attribute = event.attributes[0]
3248
+ response = self.admin_misp_connector.attach_galaxy_cluster(attribute, galaxy_cluster)
3249
+ self.assertTrue(response['saved'])
3250
+ event = self.admin_misp_connector.get_event(event.id, pythonify=True)
3251
+ attribute = event.attributes[0]
3252
+ self.assertEqual(len(attribute.galaxies), 1)
3253
+ attribute_galaxy = attribute.galaxies[0]
3254
+ # The galaxy ID should equal the galaxy from which the cluster came from
3255
+ self.assertEqual(attribute_galaxy.id, galaxy.id)
3256
+ # The galaxy cluster should equal the cluster added
3257
+ self.assertEqual(attribute_galaxy.clusters[0].id, galaxy_cluster.id)
3258
+ finally:
3259
+ self.admin_misp_connector.delete_event(event)
3260
+
3261
+ def test_analyst_data_CRUD(self) -> None:
3262
+ event = self.create_simple_event()
3263
+ try:
3264
+ fake_uuid = str(uuid4())
3265
+ new_note1 = MISPNote()
3266
+ new_note1.object_type = 'Event'
3267
+ new_note1.object_uuid = fake_uuid
3268
+ new_note1.note = 'Fake note'
3269
+ new_note1 = self.user_misp_connector.add_note(new_note1)
3270
+ # The Note should be linked even for non-existing data
3271
+ self.assertTrue(new_note1.object_uuid == fake_uuid)
3272
+
3273
+ new_note1.note = "Updated Note"
3274
+ new_note1 = self.user_misp_connector.update_note(new_note1)
3275
+ # The Note should be updatable
3276
+ self.assertTrue(new_note1.note == "Updated Note")
3277
+
3278
+ # The Note should be able to get an Opinion
3279
+ new_opinion = new_note1.add_opinion(42, 'Test Opinion')
3280
+ new_note1 = self.user_misp_connector.update_note(new_note1)
3281
+ # Fetch newly added node
3282
+ new_note1 = self.user_misp_connector.get_note(new_note1)
3283
+ # The Opinion shoud be able to be created via the Note
3284
+ self.assertTrue(new_note1.opinions[0].opinion == new_opinion.opinion)
3285
+
3286
+ response = self.user_misp_connector.delete_note(new_note1)
3287
+ # The Note should be deletable
3288
+ self.assertTrue(response['success'])
3289
+ self.assertEqual(response['message'], 'Note deleted.')
3290
+ # The Opinion should not be deleted
3291
+ opinion_resp = self.user_misp_connector.get_opinion(new_opinion)
3292
+ self.assertTrue(opinion_resp.opinion == new_opinion.opinion)
3293
+
3294
+ new_note: MISPNote = event.add_note(note='Test Note', language='en')
3295
+ new_note.distribution = 1 # Community
3296
+ event = self.user_misp_connector.add_event(event)
3297
+ # The note should be linked by Event UUID
3298
+ self.assertEqual(new_note.object_type, 'Event')
3299
+ self.assertTrue(new_note.object_uuid == event.uuid)
3300
+
3301
+ event = self.user_misp_connector.get_event(event)
3302
+ # The Note should be present on the event
3303
+ self.assertTrue(event.notes[0].object_uuid == event.uuid)
3304
+
3305
+ finally:
3306
+ self.admin_misp_connector.delete_event(event)
3307
+ try:
3308
+ self.admin_misp_connector.delete_opinion(new_opinion)
3309
+ self.admin_misp_connector.delete_note(new_note)
3310
+ self.admin_misp_connector.delete_note(new_note1) # Should already be deleted
3311
+ except Exception:
3312
+ pass
3313
+
3314
+ def test_analyst_data_ACL(self) -> None:
3315
+ event = self.create_simple_event()
3316
+ event.distribution = 2
3317
+ sg = MISPSharingGroup()
3318
+ sg.name = 'Testcases SG'
3319
+ sg.releasability = 'Testing'
3320
+ sharing_group = self.admin_misp_connector.add_sharing_group(sg, pythonify=True)
3321
+ # Chec that sharing group was created
3322
+ self.assertEqual(sharing_group.name, 'Testcases SG')
3323
+
3324
+ try:
3325
+ new_note: MISPNote = event.add_note(note='Test Note', language='en')
3326
+ new_note.distribution = 0 # Org only
3327
+ event = self.admin_misp_connector.add_event(event, pythonify=True)
3328
+
3329
+ # The note should be linked by Event UUID
3330
+ self.assertEqual(new_note.object_type, 'Event')
3331
+ self.assertEqual(event.uuid, new_note.object_uuid)
3332
+
3333
+ event = self.admin_misp_connector.get_event(event, pythonify=True)
3334
+ # The note should be visible for the creator
3335
+ self.assertEqual(len(event.notes), 1)
3336
+ self.assertTrue(new_note.note == "Test Note")
3337
+
3338
+ resp = self.user_misp_connector.get_note(new_note)
3339
+ # The note should not be visible to another org
3340
+ self.assertTrue(len(resp), 0)
3341
+
3342
+ event = self.user_misp_connector.get_event(event)
3343
+ # The Note attached to the event should not be visible for another org than the creator
3344
+ self.assertEqual(len(event.Note), 0)
3345
+
3346
+ new_note = self.admin_misp_connector.get_note(new_note, pythonify=True)
3347
+ new_note.distribution = 4
3348
+ new_note.sharing_group_id = sharing_group.id
3349
+ new_note = self.admin_misp_connector.update_note(new_note, pythonify=True)
3350
+ self.assertEqual(int(new_note.sharing_group_id), int(sharing_group.id))
3351
+
3352
+ event = self.user_misp_connector.get_event(event)
3353
+ # The Note attached to the event should not be visible for another org not part of the sharing group
3354
+ self.assertEqual(len(event.Note), 0)
3355
+
3356
+ # Add org to the sharing group
3357
+ r = self.admin_misp_connector.add_org_to_sharing_group(sharing_group,
3358
+ self.test_org, extend=True)
3359
+ self.assertEqual(r['name'], 'Organisation added to the sharing group.')
3360
+
3361
+ event = self.user_misp_connector.get_event(event)
3362
+ # The Note attached to the event should now be visible
3363
+ self.assertEqual(len(event.Note), 1)
3364
+
3365
+ new_note.note = "Updated Note"
3366
+ resp = self.user_misp_connector.update_note(new_note)
3367
+ # The Note should not be updatable by another organisation
3368
+ self.assertTrue(resp['errors'])
3369
+
3370
+ resp = self.user_misp_connector.delete_note(new_note)
3371
+ # The Note should not be deletable by another organisation
3372
+ self.assertTrue(resp['errors'])
3373
+
3374
+ organisation = MISPOrganisation()
3375
+ organisation.name = 'Fake Org'
3376
+ fake_org = self.admin_misp_connector.add_organisation(organisation, pythonify=True)
3377
+ new_note_2 = new_note.add_note('Test Note 2')
3378
+ new_note_2.orgc_uuid = fake_org.uuid
3379
+ new_note_2 = self.user_misp_connector.add_note(new_note_2)
3380
+ # Regular user should not be able to create a note on behalf of another organisation
3381
+ self.assertFalse(new_note_2.orgc_uuid == fake_org.uuid)
3382
+ # Note should have the orgc set to the use's organisation for non-privileged users
3383
+ self.assertTrue(new_note_2.orgc_uuid == self.test_org.uuid)
3384
+
3385
+ finally:
3386
+ self.admin_misp_connector.delete_event(event)
3387
+ try:
3388
+ pass
3389
+ self.admin_misp_connector.delete_sharing_group(sharing_group.id)
3390
+ self.admin_misp_connector.delete_organisation(fake_org)
3391
+ self.admin_misp_connector.delete_note(new_note)
3392
+ except Exception:
3393
+ pass
3394
+
3395
+ @unittest.skip("Internal use only")
3396
+ def missing_methods(self) -> None:
3397
+ skip = [
3398
+ "attributes/download",
3399
+ "attributes/add_attachment",
3400
+ "attributes/add_threatconnect",
3401
+ "attributes/editField",
3402
+ "attributes/viewPicture",
3403
+ "attributes/restore",
3404
+ "attributes/deleteSelected",
3405
+ "attributes/editSelected",
3406
+ "attributes/search",
3407
+ "attributes/searchAlternate",
3408
+ "attributes/checkComposites",
3409
+ "attributes/downloadAttachment",
3410
+ "attributes/returnAttributes",
3411
+ "attributes/text",
3412
+ "attributes/rpz",
3413
+ "attributes/bro",
3414
+ "attributes/reportValidationIssuesAttributes",
3415
+ "attributes/generateCorrelation",
3416
+ "attributes/getMassEditForm",
3417
+ "attributes/fetchViewValue",
3418
+ "attributes/fetchEditForm",
3419
+ "attributes/attributeReplace",
3420
+ "attributes/downloadSample",
3421
+ "attributes/pruneOrphanedAttributes",
3422
+ "attributes/checkOrphanedAttributes",
3423
+ "attributes/updateAttributeValues",
3424
+ "attributes/hoverEnrichment",
3425
+ "attributes/addTag",
3426
+ "attributes/removeTag",
3427
+ "attributes/toggleCorrelation", # Use update attribute
3428
+ "attributes/toggleToIDS", # Use update attribute
3429
+ "attributes/checkAttachments",
3430
+ "attributes/exportSearch",
3431
+ 'dashboards',
3432
+ 'decayingModel',
3433
+ "eventBlocklists/massDelete",
3434
+ "eventDelegations/view",
3435
+ "eventDelegations/index",
3436
+ "eventGraph/view",
3437
+ "eventGraph/add",
3438
+ "eventGraph/delete",
3439
+ "events/filterEventIndex",
3440
+ "events/viewEventAttributes",
3441
+ "events/removePivot",
3442
+ "events/addIOC",
3443
+ "events/add_misp_export",
3444
+ "events/merge",
3445
+ "events/unpublish",
3446
+ "events/publishSightings",
3447
+ "events/automation",
3448
+ "events/export",
3449
+ "events/downloadExport",
3450
+ "events/xml",
3451
+ "events/nids",
3452
+ "events/hids",
3453
+ "events/csv",
3454
+ "events/downloadOpenIOCEvent",
3455
+ "events/proposalEventIndex",
3456
+ "events/reportValidationIssuesEvents",
3457
+ "events/addTag",
3458
+ "events/removeTag",
3459
+ "events/saveFreeText",
3460
+ "events/stix2",
3461
+ "events/stix",
3462
+ "events/filterEventIdsForPush",
3463
+ "events/checkuuid",
3464
+ "events/pushProposals",
3465
+ "events/exportChoice",
3466
+ "events/importChoice",
3467
+ "events/upload_sample",
3468
+ "events/viewGraph",
3469
+ "events/viewEventGraph",
3470
+ "events/updateGraph",
3471
+ "events/genDistributionGraph",
3472
+ "events/getEventTimeline",
3473
+ "events/getDistributionGraph",
3474
+ "events/getEventGraphReferences",
3475
+ "events/getEventGraphTags",
3476
+ "events/getEventGraphGeneric",
3477
+ "events/getReferenceData",
3478
+ "events/getObjectTemplate",
3479
+ "events/viewGalaxyMatrix",
3480
+ "events/delegation_index",
3481
+ "events/queryEnrichment",
3482
+ "events/handleModuleResults",
3483
+ "events/importModule",
3484
+ "events/exportModule",
3485
+ "events/toggleCorrelation", # TODO
3486
+ "events/checkPublishedStatus",
3487
+ "events/pushEventToKafka",
3488
+ "events/getEventInfoById",
3489
+ "events/enrichEvent", # TODO
3490
+ "events/checkLocks",
3491
+ "events/getEditStrategy",
3492
+ "events/upload_analysis_file",
3493
+ "events/cullEmptyEvents",
3494
+ "favouriteTags/toggle", # TODO
3495
+ "favouriteTags/getToggleField", # TODO
3496
+ "feeds/feedCoverage",
3497
+ "feeds/importFeeds",
3498
+ "feeds/fetchFromAllFeeds",
3499
+ "feeds/getEvent",
3500
+ "feeds/previewIndex", # TODO
3501
+ "feeds/previewEvent", # TODO
3502
+ "feeds/enable",
3503
+ "feeds/disable",
3504
+ "feeds/fetchSelectedFromFreetextIndex",
3505
+ "feeds/toggleSelected", # TODO
3506
+ "galaxies/delete",
3507
+ "galaxies/selectGalaxy",
3508
+ "galaxies/selectGalaxyNamespace",
3509
+ "galaxies/selectCluster",
3510
+ "galaxies/attachCluster",
3511
+ "galaxies/attachMultipleClusters",
3512
+ "galaxies/viewGraph",
3513
+ "galaxies/showGalaxies",
3514
+ "galaxyClusters/index",
3515
+ "galaxyClusters/view",
3516
+ "galaxyClusters/attachToEvent",
3517
+ "galaxyClusters/detach",
3518
+ "galaxyClusters/delete",
3519
+ "galaxyClusters/viewGalaxyMatrix",
3520
+ "galaxyElements/index",
3521
+ "jobs/index",
3522
+ "jobs/getError",
3523
+ "jobs/getGenerateCorrelationProgress",
3524
+ "jobs/getProgress",
3525
+ "jobs/cache",
3526
+ "jobs/clearJobs",
3527
+ "logs/event_index",
3528
+ "admin/logs/search",
3529
+ "logs/returnDates",
3530
+ "logs/pruneUpdateLogs",
3531
+ "logs/testForStolenAttributes",
3532
+ "modules/queryEnrichment",
3533
+ "modules/index",
3534
+ "news/index",
3535
+ "news/add",
3536
+ "news/edit",
3537
+ "news/delete",
3538
+ "noticelists/toggleEnable",
3539
+ "noticelists/getToggleField",
3540
+ "noticelists/delete",
3541
+ "objectReferences/view",
3542
+ "objectTemplateElements/viewElements",
3543
+ "objectTemplates/objectMetaChoice",
3544
+ "objectTemplates/objectChoice",
3545
+ "objectTemplates/delete",
3546
+ "objectTemplates/viewElements",
3547
+ "objectTemplates/activate",
3548
+ "objectTemplates/getToggleField",
3549
+ "objects/revise_object",
3550
+ "objects/get_row",
3551
+ "objects/editField",
3552
+ "objects/fetchViewValue",
3553
+ "objects/fetchEditForm",
3554
+ "objects/quickFetchTemplateWithValidObjectAttributes",
3555
+ "objects/quickAddAttributeForm",
3556
+ "objects/orphanedObjectDiagnostics",
3557
+ "objects/proposeObjectsFromAttributes",
3558
+ "objects/groupAttributesIntoObject",
3559
+ "admin/organisations/generateuuid",
3560
+ "organisations/landingpage",
3561
+ "organisations/fetchOrgsForSG",
3562
+ "organisations/fetchSGOrgRow",
3563
+ "organisations/getUUIDs",
3564
+ "admin/organisations/merge",
3565
+ "pages/display",
3566
+ "posts/pushMessageToZMQ",
3567
+ "posts/add",
3568
+ "posts/edit",
3569
+ "posts/delete",
3570
+ "admin/regexp/add",
3571
+ "admin/regexp/index",
3572
+ "admin/regexp/edit",
3573
+ "admin/regexp/delete",
3574
+ "regexp/index",
3575
+ "admin/regexp/clean",
3576
+ "regexp/cleanRegexModifiers",
3577
+ "restClientHistory/index",
3578
+ "restClientHistory/delete",
3579
+ "roles/view",
3580
+ "admin/roles/add", # TODO
3581
+ "admin/roles/edit", # TODO
3582
+ "admin/roles/index", # TODO
3583
+ "admin/roles/delete", # TODO
3584
+ "servers/previewIndex",
3585
+ "servers/previewEvent",
3586
+ "servers/filterEventIndex",
3587
+ "servers/eventBlockRule",
3588
+ "servers/serverSettingsReloadSetting",
3589
+ "servers/startWorker", # TODO
3590
+ "servers/stopWorker", # TODO
3591
+ "servers/getWorkers", # TODO
3592
+ "servers/getSubmodulesStatus", # TODO,
3593
+ "servers/restartDeadWorkers", # TODO
3594
+ "servers/deleteFile",
3595
+ "servers/uploadFile",
3596
+ "servers/fetchServersForSG",
3597
+ "servers/postTest",
3598
+ "servers/getRemoteUser",
3599
+ "servers/startZeroMQServer",
3600
+ "servers/stopZeroMQServer",
3601
+ "servers/statusZeroMQServer",
3602
+ "servers/purgeSessions",
3603
+ "servers/clearWorkerQueue", # TODO
3604
+ "servers/getGit",
3605
+ "servers/checkout",
3606
+ "servers/ondemandAction",
3607
+ "servers/updateProgress",
3608
+ "servers/getSubmoduleQuickUpdateForm",
3609
+ "servers/updateSubmodule",
3610
+ "servers/getInstanceUUID",
3611
+ "servers/getApiInfo",
3612
+ "servers/cache",
3613
+ "servers/updateJSON",
3614
+ "servers/resetRemoteAuthKey",
3615
+ "servers/changePriority",
3616
+ "servers/releaseUpdateLock",
3617
+ "servers/viewDeprecatedFunctionUse",
3618
+ "shadowAttributes/download",
3619
+ "shadowAttributes/add_attachment",
3620
+ "shadowAttributes/discardSelected",
3621
+ "shadowAttributes/acceptSelected",
3622
+ "shadowAttributes/generateCorrelation",
3623
+ "sharingGroups/edit",
3624
+ "sharingGroups/view",
3625
+ "sightingdb/add",
3626
+ "sightingdb/edit",
3627
+ "sightingdb/delete",
3628
+ "sightingdb/index",
3629
+ "sightingdb/requestStatus",
3630
+ "sightingdb/search",
3631
+ "sightings/advanced",
3632
+ "sightings/quickAdd",
3633
+ "sightings/quickDelete",
3634
+ "sightings/viewSightings",
3635
+ "sightings/bulkSaveSightings",
3636
+ "tagCollections/add",
3637
+ "tagCollections/import",
3638
+ "tagCollections/view",
3639
+ "tagCollections/edit",
3640
+ "tagCollections/delete",
3641
+ "tagCollections/addTag",
3642
+ "tagCollections/removeTag",
3643
+ "tagCollections/index",
3644
+ "tagCollections/getRow",
3645
+ "tags/quickAdd",
3646
+ "tags/showEventTag",
3647
+ "tags/showAttributeTag",
3648
+ "tags/showTagControllerTag",
3649
+ "tags/viewTag",
3650
+ "tags/selectTaxonomy",
3651
+ "tags/selectTag",
3652
+ "tags/viewGraph",
3653
+ "tags/search",
3654
+ "tasks/index",
3655
+ "tasks/setTask",
3656
+ "taxonomies/hideTag",
3657
+ "taxonomies/unhideTag",
3658
+ "taxonomies/taxonomyMassConfirmation",
3659
+ "taxonomies/taxonomyMassHide",
3660
+ "taxonomies/taxonomyMassUnhide",
3661
+ "taxonomies/delete",
3662
+ "taxonomies/toggleRequired",
3663
+ "templateElements/index",
3664
+ "templateElements/templateElementAddChoices",
3665
+ "templateElements/add",
3666
+ "templateElements/edit",
3667
+ "templateElements/delete",
3668
+ "templates/index",
3669
+ "templates/edit",
3670
+ "templates/view",
3671
+ "templates/add",
3672
+ "templates/saveElementSorting",
3673
+ "templates/delete",
3674
+ "templates/templateChoices",
3675
+ "templates/populateEventFromTemplate",
3676
+ "templates/submitEventPopulation",
3677
+ "templates/uploadFile",
3678
+ "templates/deleteTemporaryFile",
3679
+ "threads/viewEvent",
3680
+ "threads/view",
3681
+ "threads/index",
3682
+ "userSettings/view",
3683
+ "userSettings/setHomePage",
3684
+ "users/request_API",
3685
+ "admin/users/filterUserIndex",
3686
+ "admin/users/view",
3687
+ "admin/users/edit",
3688
+ "users/updateLoginTime",
3689
+ "users/login",
3690
+ "users/routeafterlogin",
3691
+ "users/logout",
3692
+ "users/resetauthkey",
3693
+ "users/resetAllSyncAuthKeys",
3694
+ "users/histogram",
3695
+ "users/terms",
3696
+ "users/downloadTerms",
3697
+ "users/checkAndCorrectPgps",
3698
+ "admin/users/quickEmail",
3699
+ "admin/users/email",
3700
+ "users/initiatePasswordReset",
3701
+ "users/email_otp",
3702
+ "users/tagStatisticsGraph",
3703
+ "users/verifyGPG",
3704
+ "users/verifyCertificate",
3705
+ "users/searchGpgKey",
3706
+ "users/fetchGpgKey",
3707
+ "users/checkIfLoggedIn",
3708
+ "admin/users/monitor",
3709
+ "warninglists/enableWarninglist",
3710
+ "warninglists/getToggleField",
3711
+ "warninglists/delete",
3712
+ "admin/allowedlists/add",
3713
+ "admin/allowedlists/index",
3714
+ "admin/allowedlists/edit",
3715
+ "admin/allowedlists/delete",
3716
+ "allowedlists/index"
3717
+ ]
3718
+ missing = self.admin_misp_connector.get_all_functions(True)
3719
+ with open('all_missing.json', 'w') as f:
3720
+ json.dump(missing, f, indent=2)
3721
+ final_missing = []
3722
+ for m in missing:
3723
+ if any(m.startswith(s) for s in skip):
3724
+ continue
3725
+ final_missing.append(m)
3726
+ with open('plop', 'w') as f:
3727
+ json.dump(final_missing, f, indent=2)
3728
+ print(final_missing)
3729
+ print(len(final_missing))
3730
+ raise Exception()
3731
+
3732
+
3733
+ if __name__ == '__main__':
3734
+ unittest.main()