synapse 2.187.0__py311-none-any.whl → 2.188.1__py311-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 synapse might be problematic. Click here for more details.

Files changed (46) hide show
  1. synapse/cortex.py +131 -7
  2. synapse/datamodel.py +20 -4
  3. synapse/exc.py +14 -1
  4. synapse/lib/ast.py +6 -4
  5. synapse/lib/auth.py +9 -0
  6. synapse/lib/drive.py +1 -1
  7. synapse/lib/httpapi.py +2 -1
  8. synapse/lib/nexus.py +6 -0
  9. synapse/lib/node.py +5 -3
  10. synapse/lib/scrape.py +18 -104
  11. synapse/lib/storm.py +44 -28
  12. synapse/lib/stormlib/modelext.py +31 -0
  13. synapse/lib/stormlib/scrape.py +1 -4
  14. synapse/lib/stormtypes.py +17 -1
  15. synapse/lib/version.py +2 -2
  16. synapse/lib/view.py +9 -3
  17. synapse/models/base.py +27 -0
  18. synapse/models/files.py +22 -0
  19. synapse/models/inet.py +49 -4
  20. synapse/models/orgs.py +64 -2
  21. synapse/models/proj.py +1 -6
  22. synapse/models/risk.py +65 -0
  23. synapse/tests/test_cortex.py +21 -0
  24. synapse/tests/test_lib_agenda.py +13 -0
  25. synapse/tests/test_lib_auth.py +15 -0
  26. synapse/tests/test_lib_cell.py +170 -159
  27. synapse/tests/test_lib_httpapi.py +6 -0
  28. synapse/tests/test_lib_nexus.py +26 -0
  29. synapse/tests/test_lib_scrape.py +14 -6
  30. synapse/tests/test_lib_storm.py +48 -0
  31. synapse/tests/test_lib_stormlib_modelext.py +76 -1
  32. synapse/tests/test_lib_stormlib_scrape.py +0 -8
  33. synapse/tests/test_lib_stormtypes.py +1 -1
  34. synapse/tests/test_lib_trigger.py +8 -0
  35. synapse/tests/test_lib_view.py +24 -0
  36. synapse/tests/test_model_base.py +11 -0
  37. synapse/tests/test_model_files.py +19 -0
  38. synapse/tests/test_model_inet.py +33 -0
  39. synapse/tests/test_model_orgs.py +39 -0
  40. synapse/tests/test_model_proj.py +11 -1
  41. synapse/tests/test_model_risk.py +32 -0
  42. {synapse-2.187.0.dist-info → synapse-2.188.1.dist-info}/METADATA +1 -1
  43. {synapse-2.187.0.dist-info → synapse-2.188.1.dist-info}/RECORD +46 -46
  44. {synapse-2.187.0.dist-info → synapse-2.188.1.dist-info}/WHEEL +1 -1
  45. {synapse-2.187.0.dist-info → synapse-2.188.1.dist-info}/LICENSE +0 -0
  46. {synapse-2.187.0.dist-info → synapse-2.188.1.dist-info}/top_level.txt +0 -0
synapse/models/orgs.py CHANGED
@@ -249,8 +249,24 @@ class OuModule(s_module.CoreModule):
249
249
  'doc': 'Vital statistics about an org for a given time period.',
250
250
  }),
251
251
  ('ou:opening', ('guid', {}), {
252
- 'doc': 'A job/work opening within an org.',
253
- }),
252
+ 'doc': 'A job/work opening within an org.'}),
253
+
254
+ ('ou:candidate:method:taxonomy', ('taxonomy', {}), {
255
+ 'interfaces': ('meta:taxonomy',),
256
+ 'doc': 'A taxonomy of methods by which a candidate came under consideration.'}),
257
+
258
+ ('ou:candidate', ('guid', {}), {
259
+ 'doc': 'A candidate being considered for a role within an organization.',
260
+ 'display': {
261
+ 'columns': (
262
+ {'type': 'prop', 'opts': {'name': 'contact::name'}},
263
+ {'type': 'prop', 'opts': {'name': 'contact::email'}},
264
+ {'type': 'prop', 'opts': {'name': 'submitted'}},
265
+ {'type': 'prop', 'opts': {'name': 'org::name'}},
266
+ {'type': 'prop', 'opts': {'name': 'opening::jobtitle'}},
267
+ ),
268
+ }}),
269
+
254
270
  ('ou:jobtype', ('taxonomy', {}), {
255
271
  'ex': 'it.dev.python',
256
272
  'doc': 'A taxonomy of job types.',
@@ -287,6 +303,8 @@ class OuModule(s_module.CoreModule):
287
303
  'doc': 'The campaign used the technique.'}),
288
304
  (('ou:org', 'uses', 'ou:technique'), {
289
305
  'doc': 'The org uses the technique.'}),
306
+ (('risk:vuln', 'uses', 'ou:technique'), {
307
+ 'doc': 'The vulnerability uses the technique.'}),
290
308
 
291
309
  (('ou:org', 'uses', None), {
292
310
  'doc': 'The ou:org makes use of the target node.'}),
@@ -354,6 +372,44 @@ class OuModule(s_module.CoreModule):
354
372
  }),
355
373
  # TODO a way to encode/normalize requirements.
356
374
  )),
375
+ ('ou:candidate:method:taxonomy', {}, ()),
376
+ ('ou:candidate', {}, (
377
+
378
+ ('org', ('ou:org', {}), {
379
+ 'doc': 'The organization considering the candidate.'}),
380
+
381
+ ('contact', ('ps:contact', {}), {
382
+ 'doc': 'The contact information of the candidate.'}),
383
+
384
+ ('method', ('ou:candidate:method:taxonomy', {}), {
385
+ 'doc': 'The method by which the candidate came under consideration.'}),
386
+
387
+ ('submitted', ('time', {}), {
388
+ 'doc': 'The time the candidate was submitted for consideration.'}),
389
+
390
+ ('intro', ('str', {'strip': True}), {
391
+ 'doc': 'An introduction or cover letter text submitted by the candidate.'}),
392
+
393
+ ('resume', ('file:bytes', {}), {
394
+ 'doc': "The candidate's resume or CV."}),
395
+
396
+ ('opening', ('ou:opening', {}), {
397
+ 'doc': 'The opening that the candidate is being considered for.'}),
398
+
399
+ ('agent', ('ps:contact', {}), {
400
+ 'doc': 'The contact information of an agent who advocates for the candidate.'}),
401
+
402
+ ('recruiter', ('ps:contact', {}), {
403
+ 'doc': 'The contact information of a recruiter who works on behalf of the organization.'}),
404
+
405
+ ('attachments', ('array', {'type': 'file:attachment', 'sorted': True, 'uniq': True}), {
406
+ 'doc': 'An array of additional files submitted by the candidate.'}),
407
+
408
+ # TODO: doc:questionare / responses
409
+ # TODO: :skills=[<ps:skill>]? vs :contact -> ps:proficiency?
410
+ # TODO: proj:task to track evaluation of the candidate?
411
+
412
+ )),
357
413
  ('ou:vitals', {}, (
358
414
 
359
415
  ('asof', ('time', {}), {
@@ -876,6 +932,12 @@ class OuModule(s_module.CoreModule):
876
932
  ('names', ('array', {'type': 'ou:industryname', 'uniq': True, 'sorted': True}), {
877
933
  'doc': 'An array of alternative names for the industry.'}),
878
934
 
935
+ ('reporter', ('ou:org', {}), {
936
+ 'doc': 'The organization reporting on the industry.'}),
937
+
938
+ ('reporter:name', ('ou:name', {}), {
939
+ 'doc': 'The name of the organization reporting on the industry.'}),
940
+
879
941
  ('subs', ('array', {'type': 'ou:industry', 'split': ',', 'uniq': True, 'sorted': True}), {
880
942
  'deprecated': True,
881
943
  'doc': 'Deprecated. Please use ou:industry:type taxonomy.'}),
synapse/models/proj.py CHANGED
@@ -16,10 +16,9 @@ class ProjectModule(s_module.CoreModule):
16
16
 
17
17
  async def initCoreModule(self):
18
18
  self.model.form('proj:project').onAdd(self._onAddProj)
19
- self.model.form('proj:project').onDel(self._onDelProj)
20
19
 
21
20
  async def _onAddProj(self, node):
22
- # ref counts on authgates?
21
+ # TODO: remove all storm:project authgates in 3.x migration
23
22
  gateiden = node.ndef[1]
24
23
 
25
24
  await self.core.auth.addAuthGate(node.ndef[1], 'storm:project')
@@ -29,10 +28,6 @@ class ProjectModule(s_module.CoreModule):
29
28
  rule = (True, ('project', 'admin'))
30
29
  await node.snap.user.addRule(rule, gateiden=gateiden)
31
30
 
32
- async def _onDelProj(self, node):
33
- gateiden = node.ndef[1]
34
- await self.core.auth.delAuthGate(gateiden)
35
-
36
31
  def getModelDefs(self):
37
32
  return (
38
33
 
synapse/models/risk.py CHANGED
@@ -159,6 +159,27 @@ class RiskModule(s_module.CoreModule):
159
159
  ('risk:extortion', ('guid', {}), {
160
160
  'doc': 'An event where an attacker attempted to extort a victim.'}),
161
161
 
162
+ ('risk:outage:cause:taxonomy', ('taxonomy', {}), {
163
+ 'interfaces': ('meta:taxonomy',),
164
+ 'doc': 'An outage cause taxonomy.'}),
165
+
166
+ ('risk:outage:type:taxonomy', ('taxonomy', {}), {
167
+ 'interfaces': ('meta:taxonomy',),
168
+ 'doc': 'An outage type taxonomy.'}),
169
+
170
+ ('risk:outage', ('guid', {}), {
171
+ 'display': {
172
+ 'columns': (
173
+ {'type': 'prop', 'opts': {'name': 'name'}},
174
+ {'type': 'prop', 'opts': {'name': 'type'}},
175
+ {'type': 'prop', 'opts': {'name': 'cause'}},
176
+ {'type': 'prop', 'opts': {'name': 'period'}},
177
+ {'type': 'prop', 'opts': {'name': 'provider:name'}},
178
+ {'type': 'prop', 'opts': {'name': 'reporter:name'}},
179
+ ),
180
+ },
181
+ 'doc': 'An outage event which affected resource availability.'}),
182
+
162
183
  ('risk:extortion:type:taxonomy', ('taxonomy', {}), {
163
184
  'interfaces': ('meta:taxonomy',),
164
185
  'doc': 'A taxonomy of extortion event types.'}),
@@ -185,6 +206,8 @@ class RiskModule(s_module.CoreModule):
185
206
  'doc': 'The threat cluster uses the vulnerability.'}),
186
207
  (('risk:tool:software', 'uses', 'risk:vuln'), {
187
208
  'doc': 'The tool uses the vulnerability.'}),
209
+ (('ou:technique', 'uses', 'risk:vuln'), {
210
+ 'doc': 'The technique uses the vulnerability.'}),
188
211
 
189
212
  (('risk:attack', 'targets', 'ou:industry'), {
190
213
  'doc': 'The attack targeted the industry.'}),
@@ -224,6 +247,15 @@ class RiskModule(s_module.CoreModule):
224
247
 
225
248
  (('risk:extortion', 'leveraged', None), {
226
249
  'doc': 'The extortion event was based on attacker access to the target node.'}),
250
+
251
+ (('meta:event', 'caused', 'risk:outage'), {
252
+ 'doc': 'The event caused the outage.'}),
253
+
254
+ (('risk:attack', 'caused', 'risk:outage'), {
255
+ 'doc': 'The attack caused the outage.'}),
256
+
257
+ (('risk:outage', 'impacted', None), {
258
+ 'doc': 'The outage event impacted the availability of the target node.'}),
227
259
  ),
228
260
  'forms': (
229
261
 
@@ -1025,6 +1057,39 @@ class RiskModule(s_module.CoreModule):
1025
1057
 
1026
1058
  )),
1027
1059
 
1060
+ ('risk:outage:type:taxonomy', {}, ()),
1061
+ ('risk:outage:cause:taxonomy', {}, ()),
1062
+ ('risk:outage', {}, (
1063
+
1064
+ ('name', ('str', {'lower': True, 'onespace': True}), {
1065
+ 'doc': 'A name for the outage event.'}),
1066
+
1067
+ ('period', ('ival', {}), {
1068
+ 'doc': 'The time period where the outage impacted availability.'}),
1069
+
1070
+ ('type', ('risk:outage:type:taxonomy', {}), {
1071
+ 'ex': 'service.power',
1072
+ 'doc': 'The type of outage.'}),
1073
+
1074
+ ('cause', ('risk:outage:cause:taxonomy', {}), {
1075
+ 'ex': 'nature.earthquake',
1076
+ 'doc': 'The outage cause type.'}),
1077
+
1078
+ ('provider', ('ou:org', {}), {
1079
+ 'doc': 'The organization which experienced the outage event.'}),
1080
+
1081
+ ('provider:name', ('ou:name', {}), {
1082
+ 'doc': 'The name of the organization which experienced the outage event.'}),
1083
+
1084
+ ('reporter', ('ou:org', {}), {
1085
+ 'doc': 'The organization reporting on the outage event.'}),
1086
+
1087
+ ('reporter:name', ('ou:name', {}), {
1088
+ 'doc': 'The name of the organization reporting on the outage event.'}),
1089
+ )),
1090
+
1091
+ # TODO risk:outage:vitals to track outage stats over time
1092
+
1028
1093
  ('risk:extortion:type:taxonomy', {}, ()),
1029
1094
  ('risk:extortion', {}, (
1030
1095
 
@@ -5801,6 +5801,13 @@ class CortexBasicTest(s_t_utils.SynTest):
5801
5801
  # but getTypeNorm won't handle that
5802
5802
  await self.asyncraises(s_exc.NoSuchType, core.getTypeNorm('test:str:tick', '3001'))
5803
5803
 
5804
+ # specify typeopts to getTypeNorm/getPropNorm
5805
+ norm, info = await prox.getTypeNorm('array', (' TIME ', ' pass ', ' the '), {'uniq': True, 'sorted': True, 'type': 'str', 'typeopts': {'strip': True, 'lower': True}})
5806
+ self.eq(norm, ('pass', 'the', 'time'))
5807
+
5808
+ norm, info = await prox.getPropNorm('test:comp', "1234:comedy", {'sepr': ':'})
5809
+ self.eq(norm, (1234, "comedy"))
5810
+
5804
5811
  # getTypeNorm can norm types which aren't defined as forms/props
5805
5812
  norm, info = await core.getTypeNorm('test:lower', 'ASDF')
5806
5813
  self.eq(norm, 'asdf')
@@ -6191,7 +6198,17 @@ class CortexBasicTest(s_t_utils.SynTest):
6191
6198
  with self.raises(s_exc.DupEdgeType):
6192
6199
  await core.addEdge(('test:int', '_goes', None), {})
6193
6200
 
6201
+ await core.addType('_test:type', 'str', {}, {'interfaces': ['taxonomy']})
6202
+ self.eq(['meta:taxonomy'], core.model.type('_test:type').info.get('interfaces'))
6203
+
6204
+ with self.raises(s_exc.NoSuchType):
6205
+ await core.addType('_test:newp', 'newp', {}, {})
6206
+
6207
+ with self.raises(s_exc.BadTypeDef):
6208
+ await core.addType('_test:newp', 'array', {'type': 'newp'}, {})
6209
+
6194
6210
  # manually edit in borked entries
6211
+ core.exttypes.set('_type:bork', ('_type:bork', None, None, None))
6195
6212
  core.extforms.set('_hehe:bork', ('_hehe:bork', None, None, None))
6196
6213
  core.extedges.set(s_common.guid('newp'), ((None, '_does', 'newp'), {}))
6197
6214
 
@@ -6219,6 +6236,7 @@ class CortexBasicTest(s_t_utils.SynTest):
6219
6236
 
6220
6237
  await core.nodes('._woot [ -._woot ]')
6221
6238
 
6239
+ self.nn(core.model.type('_test:type'))
6222
6240
  self.nn(core.model.prop('._woot'))
6223
6241
  self.nn(core.model.prop('inet:ipv4._woot'))
6224
6242
  self.nn(core.model.form('inet:ipv4').prop('._woot'))
@@ -6256,6 +6274,9 @@ class CortexBasicTest(s_t_utils.SynTest):
6256
6274
  with self.raises(s_exc.NoSuchEdge):
6257
6275
  await core.delEdge(('newp', 'newp', 'newp'))
6258
6276
 
6277
+ with self.raises(s_exc.NoSuchType):
6278
+ await core.delType('_newp')
6279
+
6259
6280
  await core._delEdge(('newp', 'newp', 'newp'))
6260
6281
 
6261
6282
  await core.nodes('_hehe:haha [ -:visi ]')
@@ -432,6 +432,19 @@ class AgendaTest(s_t_utils.SynTest):
432
432
 
433
433
  self.eq(2, appt.startcount)
434
434
 
435
+ # Can't use an existing authgate iden
436
+ viewiden = core.getView().iden
437
+ cdef = {'creator': core.auth.rootuser.iden,
438
+ 'storm': '[test:str=bar]',
439
+ 'reqs': {'hour': 10},
440
+ 'incunit': 'dayofweek',
441
+ 'incvals': (2, 4),
442
+ 'iden': viewiden}
443
+ await self.asyncraises(s_exc.DupIden, core.addCronJob(cdef))
444
+ await core.delCronJob(viewiden)
445
+
446
+ self.nn(core.getAuthGate(viewiden))
447
+
435
448
  async def test_agenda_persistence(self):
436
449
  ''' Test we can make/change/delete appointments and they are persisted to storage '''
437
450
 
@@ -31,6 +31,21 @@ class AuthTest(s_test.SynTest):
31
31
  with self.raises(s_exc.DupRoleName):
32
32
  await auth.addRole('ninjas')
33
33
 
34
+ viewiden = core.getView().iden
35
+ with self.raises(s_exc.DupIden):
36
+ await auth.addUser('view', iden=viewiden)
37
+
38
+ with self.raises(s_exc.NoSuchUser):
39
+ await auth.delUser(viewiden)
40
+ self.nn(core.auth.getAuthGate(viewiden))
41
+
42
+ with self.raises(s_exc.DupIden):
43
+ await auth.addRole('view', iden=viewiden)
44
+
45
+ with self.raises(s_exc.NoSuchRole):
46
+ await auth.delRole(viewiden)
47
+ self.nn(core.auth.getAuthGate(viewiden))
48
+
34
49
  self.none(await auth._addUser(user.iden, 'visi@vertex.link'))
35
50
  self.none(await auth._addRole(user.iden, 'ninjas'))
36
51