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
@@ -29,6 +29,7 @@ import synapse.lib.coro as s_coro
29
29
  import synapse.lib.link as s_link
30
30
  import synapse.lib.drive as s_drive
31
31
  import synapse.lib.nexus as s_nexus
32
+ import synapse.lib.config as s_config
32
33
  import synapse.lib.certdir as s_certdir
33
34
  import synapse.lib.msgpack as s_msgpack
34
35
  import synapse.lib.version as s_version
@@ -160,8 +161,9 @@ testDataSchema_v0 = {
160
161
  'properties': {
161
162
  'type': {'type': 'string'},
162
163
  'size': {'type': 'number'},
164
+ 'stuff': {'type': ['number', 'null'], 'default': None}
163
165
  },
164
- 'required': ['type', 'size'],
166
+ 'required': ['type', 'size', 'stuff'],
165
167
  'additionalProperties': False,
166
168
  }
167
169
 
@@ -170,6 +172,7 @@ testDataSchema_v1 = {
170
172
  'properties': {
171
173
  'type': {'type': 'string'},
172
174
  'size': {'type': 'number'},
175
+ 'stuff': {'type': ['number', 'null'], 'default': None},
173
176
  'woot': {'type': 'string'},
174
177
  },
175
178
  'required': ['type', 'size', 'woot'],
@@ -180,202 +183,210 @@ class CellTest(s_t_utils.SynTest):
180
183
 
181
184
  async def test_cell_drive(self):
182
185
 
183
- async with self.getTestCell() as cell:
186
+ with self.getTestDir() as dirn:
187
+ async with self.getTestCell(dirn=dirn) as cell:
184
188
 
185
- with self.raises(s_exc.BadName):
186
- s_drive.reqValidName('A' * 512)
189
+ with self.raises(s_exc.BadName):
190
+ s_drive.reqValidName('A' * 512)
187
191
 
188
- info = {'name': 'users'}
189
- pathinfo = await cell.addDriveItem(info)
192
+ info = {'name': 'users'}
193
+ pathinfo = await cell.addDriveItem(info)
190
194
 
191
- info = {'name': 'root'}
192
- pathinfo = await cell.addDriveItem(info, path='users')
195
+ info = {'name': 'root'}
196
+ pathinfo = await cell.addDriveItem(info, path='users')
193
197
 
194
- with self.raises(s_exc.DupIden):
195
- await cell.drive.addItemInfo(pathinfo[-1], path='users')
198
+ with self.raises(s_exc.DupIden):
199
+ await cell.drive.addItemInfo(pathinfo[-1], path='users')
196
200
 
197
- rootdir = pathinfo[-1].get('iden')
198
- self.eq(0, pathinfo[-1].get('kids'))
201
+ rootdir = pathinfo[-1].get('iden')
202
+ self.eq(0, pathinfo[-1].get('kids'))
199
203
 
200
- info = {'name': 'win32k.sys', 'type': 'hehe'}
201
- with self.raises(s_exc.NoSuchType):
202
- info = await cell.addDriveItem(info, reldir=rootdir)
204
+ info = {'name': 'win32k.sys', 'type': 'hehe'}
205
+ with self.raises(s_exc.NoSuchType):
206
+ info = await cell.addDriveItem(info, reldir=rootdir)
207
+
208
+ infos = [i async for i in cell.getDriveKids(s_drive.rootdir)]
209
+ self.len(1, infos)
210
+ self.eq(1, infos[0].get('kids'))
211
+ self.eq('users', infos[0].get('name'))
212
+
213
+ # TODO how to handle iden match with additional property mismatch
203
214
 
204
- infos = [i async for i in cell.getDriveKids(s_drive.rootdir)]
205
- self.len(1, infos)
206
- self.eq(1, infos[0].get('kids'))
207
- self.eq('users', infos[0].get('name'))
215
+ self.true(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0))
216
+ self.true(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=1))
217
+ self.false(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=1))
208
218
 
209
- # TODO how to handle iden match with additional property mismatch
219
+ with self.raises(s_exc.BadVersion):
220
+ await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0)
210
221
 
211
- self.true(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0))
212
- self.true(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=1))
213
- self.false(await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=1))
222
+ info = {'name': 'win32k.sys', 'type': 'woot'}
223
+ info = await cell.addDriveItem(info, reldir=rootdir)
214
224
 
215
- with self.raises(s_exc.BadVersion):
216
- await cell.drive.setTypeSchema('woot', testDataSchema_v0, vers=0)
225
+ iden = info[-1].get('iden')
217
226
 
218
- info = {'name': 'win32k.sys', 'type': 'woot'}
219
- info = await cell.addDriveItem(info, reldir=rootdir)
227
+ tick = s_common.now()
228
+ rootuser = cell.auth.rootuser.iden
220
229
 
221
- iden = info[-1].get('iden')
230
+ with self.raises(s_exc.SchemaViolation):
231
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
232
+ await cell.setDriveData(iden, versinfo, {'newp': 'newp'})
222
233
 
223
- tick = s_common.now()
224
- rootuser = cell.auth.rootuser.iden
234
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
235
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 20, 'stuff': 12})
236
+ self.eq(info.get('version'), (1, 1, 0))
237
+ self.eq(versinfo.get('version'), (1, 1, 0))
225
238
 
226
- with self.raises(s_exc.SchemaViolation):
227
239
  versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
228
- await cell.setDriveData(iden, versinfo, {'newp': 'newp'})
229
-
230
- versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
231
- info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 20})
232
- self.eq(info.get('version'), (1, 1, 0))
233
- self.eq(versinfo.get('version'), (1, 1, 0))
234
-
235
- versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
236
- info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
237
- self.eq(info.get('version'), (1, 1, 0))
238
- self.eq(versinfo.get('version'), (1, 0, 0))
239
-
240
- versinfo10, data10 = await cell.getDriveData(iden, vers=(1, 0, 0))
241
- self.eq(versinfo10.get('updated'), tick)
242
- self.eq(versinfo10.get('updater'), rootuser)
243
- self.eq(versinfo10.get('version'), (1, 0, 0))
244
-
245
- versinfo11, data11 = await cell.getDriveData(iden, vers=(1, 1, 0))
246
- self.eq(versinfo11.get('updated'), tick + 10)
247
- self.eq(versinfo11.get('updater'), rootuser)
248
- self.eq(versinfo11.get('version'), (1, 1, 0))
249
-
250
- versions = [vers async for vers in cell.getDriveDataVersions(iden)]
251
- self.len(2, versions)
252
- self.eq(versions[0], versinfo11)
253
- self.eq(versions[1], versinfo10)
254
-
255
- info = await cell.delDriveData(iden, vers=(0, 0, 0))
256
-
257
- versions = [vers async for vers in cell.getDriveDataVersions(iden)]
258
- self.len(2, versions)
259
- self.eq(versions[0], versinfo11)
260
- self.eq(versions[1], versinfo10)
261
-
262
- info = await cell.delDriveData(iden, vers=(1, 1, 0))
263
- self.eq(info.get('updated'), tick)
264
- self.eq(info.get('version'), (1, 0, 0))
265
-
266
- info = await cell.delDriveData(iden, vers=(1, 0, 0))
267
- self.eq(info.get('size'), 0)
268
- self.eq(info.get('version'), (0, 0, 0))
269
- self.none(info.get('updated'))
270
- self.none(info.get('updater'))
271
-
272
- # repopulate a couple data versions to test migration and delete
273
- versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
274
- info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
275
- versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
276
- info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17})
277
- self.eq(versinfo, (await cell.getDriveData(iden))[0])
278
-
279
- # This will be done by the cell in a cell storage version migration...
280
- async def migrate_v1(info, versinfo, data):
281
- data['woot'] = 'woot'
282
- return data
283
-
284
- await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
285
-
286
- versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
287
- self.eq('woot', data.get('woot'))
288
-
289
- versinfo, data = await cell.getDriveData(iden, vers=(1, 1, 0))
290
- self.eq('woot', data.get('woot'))
240
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0, 'stuff': 13})
241
+ self.eq(info.get('version'), (1, 1, 0))
242
+ self.eq(versinfo.get('version'), (1, 0, 0))
243
+
244
+ versinfo10, data10 = await cell.getDriveData(iden, vers=(1, 0, 0))
245
+ self.eq(versinfo10.get('updated'), tick)
246
+ self.eq(versinfo10.get('updater'), rootuser)
247
+ self.eq(versinfo10.get('version'), (1, 0, 0))
248
+
249
+ versinfo11, data11 = await cell.getDriveData(iden, vers=(1, 1, 0))
250
+ self.eq(versinfo11.get('updated'), tick + 10)
251
+ self.eq(versinfo11.get('updater'), rootuser)
252
+ self.eq(versinfo11.get('version'), (1, 1, 0))
253
+
254
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
255
+ self.len(2, versions)
256
+ self.eq(versions[0], versinfo11)
257
+ self.eq(versions[1], versinfo10)
258
+
259
+ info = await cell.delDriveData(iden, vers=(0, 0, 0))
260
+
261
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
262
+ self.len(2, versions)
263
+ self.eq(versions[0], versinfo11)
264
+ self.eq(versions[1], versinfo10)
265
+
266
+ info = await cell.delDriveData(iden, vers=(1, 1, 0))
267
+ self.eq(info.get('updated'), tick)
268
+ self.eq(info.get('version'), (1, 0, 0))
269
+
270
+ info = await cell.delDriveData(iden, vers=(1, 0, 0))
271
+ self.eq(info.get('size'), 0)
272
+ self.eq(info.get('version'), (0, 0, 0))
273
+ self.none(info.get('updated'))
274
+ self.none(info.get('updater'))
275
+
276
+ # repopulate a couple data versions to test migration and delete
277
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
278
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0, 'stuff': 14})
279
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
280
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17, 'stuff': 15})
281
+ self.eq(versinfo, (await cell.getDriveData(iden))[0])
291
282
 
292
- with self.raises(s_exc.NoSuchIden):
293
- await cell.reqDriveInfo('d7d6107b200e2c039540fc627bc5537d')
283
+ # This will be done by the cell in a cell storage version migration...
284
+ async def migrate_v1(info, versinfo, data):
285
+ data['woot'] = 'woot'
286
+ return data
294
287
 
295
- with self.raises(s_exc.TypeMismatch):
296
- await cell.getDriveInfo(iden, typename='newp')
288
+ await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
297
289
 
298
- self.nn(await cell.getDriveInfo(iden))
299
- self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
290
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
291
+ self.eq('woot', data.get('woot'))
300
292
 
301
- await cell.delDriveData(iden)
302
- self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
293
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 1, 0))
294
+ self.eq('woot', data.get('woot'))
303
295
 
304
- await cell.delDriveInfo(iden)
296
+ with self.raises(s_exc.NoSuchIden):
297
+ await cell.reqDriveInfo('d7d6107b200e2c039540fc627bc5537d')
305
298
 
306
- self.none(await cell.getDriveInfo(iden))
307
- self.len(0, [vers async for vers in cell.getDriveDataVersions(iden)])
299
+ with self.raises(s_exc.TypeMismatch):
300
+ await cell.getDriveInfo(iden, typename='newp')
308
301
 
309
- with self.raises(s_exc.NoSuchPath):
310
- await cell.getDrivePath('users/root/win32k.sys')
302
+ self.nn(await cell.getDriveInfo(iden))
303
+ self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
311
304
 
312
- pathinfo = await cell.addDrivePath('foo/bar/baz')
313
- self.len(3, pathinfo)
314
- self.eq('foo', pathinfo[0].get('name'))
315
- self.eq(1, pathinfo[0].get('kids'))
316
- self.eq('bar', pathinfo[1].get('name'))
317
- self.eq(1, pathinfo[1].get('kids'))
318
- self.eq('baz', pathinfo[2].get('name'))
319
- self.eq(0, pathinfo[2].get('kids'))
305
+ await cell.delDriveData(iden)
306
+ self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
320
307
 
321
- self.eq(pathinfo, await cell.addDrivePath('foo/bar/baz'))
308
+ await cell.delDriveInfo(iden)
322
309
 
323
- baziden = pathinfo[2].get('iden')
324
- self.eq(pathinfo, await cell.drive.getItemPath(baziden))
310
+ self.none(await cell.getDriveInfo(iden))
311
+ self.len(0, [vers async for vers in cell.getDriveDataVersions(iden)])
325
312
 
326
- info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
327
- self.eq(3, info['perm']['users'][rootuser])
313
+ with self.raises(s_exc.NoSuchPath):
314
+ await cell.getDrivePath('users/root/win32k.sys')
328
315
 
329
- with self.raises(s_exc.NoSuchIden):
330
- # s_drive.rootdir is all 00s... ;)
331
- await cell.setDriveInfoPerm(s_drive.rootdir, {'users': {}, 'roles': {}})
316
+ pathinfo = await cell.addDrivePath('foo/bar/baz')
317
+ self.len(3, pathinfo)
318
+ self.eq('foo', pathinfo[0].get('name'))
319
+ self.eq(1, pathinfo[0].get('kids'))
320
+ self.eq('bar', pathinfo[1].get('name'))
321
+ self.eq(1, pathinfo[1].get('kids'))
322
+ self.eq('baz', pathinfo[2].get('name'))
323
+ self.eq(0, pathinfo[2].get('kids'))
324
+
325
+ self.eq(pathinfo, await cell.addDrivePath('foo/bar/baz'))
326
+
327
+ baziden = pathinfo[2].get('iden')
328
+ self.eq(pathinfo, await cell.drive.getItemPath(baziden))
329
+
330
+ info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
331
+ self.eq(3, info['perm']['users'][rootuser])
332
+
333
+ with self.raises(s_exc.NoSuchIden):
334
+ # s_drive.rootdir is all 00s... ;)
335
+ await cell.setDriveInfoPerm(s_drive.rootdir, {'users': {}, 'roles': {}})
336
+
337
+ await cell.addDrivePath('hehe/haha')
338
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
332
339
 
333
- await cell.addDrivePath('hehe/haha')
334
- pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
340
+ self.eq('hoho', pathinfo[-1].get('name'))
341
+ self.eq(baziden, pathinfo[-1].get('iden'))
335
342
 
336
- self.eq('hoho', pathinfo[-1].get('name'))
337
- self.eq(baziden, pathinfo[-1].get('iden'))
343
+ self.true(await cell.drive.hasPathInfo('hehe/haha/hoho'))
344
+ self.false(await cell.drive.hasPathInfo('foo/bar/baz'))
338
345
 
339
- self.true(await cell.drive.hasPathInfo('hehe/haha/hoho'))
340
- self.false(await cell.drive.hasPathInfo('foo/bar/baz'))
346
+ pathinfo = await cell.getDrivePath('foo/bar')
347
+ self.eq(0, pathinfo[-1].get('kids'))
341
348
 
342
- pathinfo = await cell.getDrivePath('foo/bar')
343
- self.eq(0, pathinfo[-1].get('kids'))
349
+ pathinfo = await cell.getDrivePath('hehe/haha')
350
+ self.eq(1, pathinfo[-1].get('kids'))
344
351
 
345
- pathinfo = await cell.getDrivePath('hehe/haha')
346
- self.eq(1, pathinfo[-1].get('kids'))
352
+ with self.raises(s_exc.DupName):
353
+ iden = pathinfo[-2].get('iden')
354
+ name = pathinfo[-1].get('name')
355
+ cell.drive.reqFreeStep(iden, name)
347
356
 
348
- with self.raises(s_exc.DupName):
349
- iden = pathinfo[-2].get('iden')
350
- name = pathinfo[-1].get('name')
351
- cell.drive.reqFreeStep(iden, name)
357
+ walks = [item async for item in cell.drive.walkPathInfo('hehe')]
358
+ self.len(3, walks)
359
+ # confirm walked paths are yielded depth first...
360
+ self.eq('hoho', walks[0].get('name'))
361
+ self.eq('haha', walks[1].get('name'))
362
+ self.eq('hehe', walks[2].get('name'))
352
363
 
353
- walks = [item async for item in cell.drive.walkPathInfo('hehe')]
354
- self.len(3, walks)
355
- # confirm walked paths are yielded depth first...
356
- self.eq('hoho', walks[0].get('name'))
357
- self.eq('haha', walks[1].get('name'))
358
- self.eq('hehe', walks[2].get('name'))
364
+ iden = walks[2].get('iden')
365
+ walks = [item async for item in cell.drive.walkItemInfo(iden)]
366
+ self.len(3, walks)
367
+ self.eq('hoho', walks[0].get('name'))
368
+ self.eq('haha', walks[1].get('name'))
369
+ self.eq('hehe', walks[2].get('name'))
359
370
 
360
- iden = walks[2].get('iden')
361
- walks = [item async for item in cell.drive.walkItemInfo(iden)]
362
- self.len(3, walks)
363
- self.eq('hoho', walks[0].get('name'))
364
- self.eq('haha', walks[1].get('name'))
365
- self.eq('hehe', walks[2].get('name'))
371
+ self.none(cell.drive.getTypeSchema('newp'))
366
372
 
367
- self.none(cell.drive.getTypeSchema('newp'))
373
+ cell.drive.validators.pop('woot')
374
+ self.nn(cell.drive.getTypeValidator('woot'))
368
375
 
369
- cell.drive.validators.pop('woot')
370
- self.nn(cell.drive.getTypeValidator('woot'))
376
+ # move to root dir
377
+ pathinfo = await cell.setDriveInfoPath(baziden, 'zipzop')
378
+ self.len(1, pathinfo)
379
+ self.eq(s_drive.rootdir, pathinfo[-1].get('parent'))
371
380
 
372
- # move to root dir
373
- pathinfo = await cell.setDriveInfoPath(baziden, 'zipzop')
374
- self.len(1, pathinfo)
375
- self.eq(s_drive.rootdir, pathinfo[-1].get('parent'))
381
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
382
+ self.len(3, pathinfo)
376
383
 
377
- pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
378
- self.len(3, pathinfo)
384
+ async with self.getTestCell(dirn=dirn) as cell:
385
+ data = {'type': 'woot', 'size': 20, 'stuff': 12, 'woot': 'woot'}
386
+ # explicitly clear out the cache JsValidators, otherwise we get the cached, pre-msgpack
387
+ # version of the validator, which will be correct and skip the point of this test.
388
+ s_config._JsValidators.clear()
389
+ cell.drive.reqValidData('woot', data)
379
390
 
380
391
  async def test_cell_auth(self):
381
392
 
@@ -2993,7 +3004,7 @@ class CellTest(s_t_utils.SynTest):
2993
3004
  self.eq('barprof', valu)
2994
3005
 
2995
3006
  msgs = await core.stormlist('cron.list')
2996
- self.stormIsInPrint('visi 8437c35a', msgs)
3007
+ self.stormIsInPrint(' visi 8437c35a.. ', msgs)
2997
3008
  self.stormIsInPrint('[tel:mob:telem=*]', msgs)
2998
3009
 
2999
3010
  msgs = await core.stormlist('dmon.list')
@@ -777,6 +777,12 @@ class HttpApiTest(s_tests.SynTest):
777
777
  retn = await resp.json()
778
778
  self.eq('MissingField', retn.get('code'))
779
779
 
780
+ body = {'prop': 'test:comp', 'value': '3^foobar', 'typeopts': {'sepr': '^'}}
781
+ async with sess.get(f'https://localhost:{port}/api/v1/model/norm', json=body) as resp:
782
+ retn = await resp.json()
783
+ self.eq('ok', retn.get('status'))
784
+ self.eq([3, 'foobar'], retn['result']['norm'])
785
+
780
786
  # Norm via POST
781
787
  body = {'prop': 'inet:ipv4', 'value': '1.2.3.4'}
782
788
  async with sess.post(f'https://localhost:{port}/api/v1/model/norm', json=body) as resp:
@@ -310,6 +310,32 @@ class NexusTest(s_t_utils.SynTest):
310
310
  await cell01.sync()
311
311
  self.isin(s_nexus.leaderversion, cell01.nexsroot.writeholds)
312
312
 
313
+ cell00.getCellInfo = getCellInfo
314
+
315
+ # test case where a mirror which is updated first may push events
316
+ # the leader does not yet have handlers for
317
+ async with await s_cell.Cell.anit(dirn=path) as cell01:
318
+ cell01.nexsiden = 'newp'
319
+ with self.raises(s_exc.NoSuchIden) as cm:
320
+ await cell01.sync()
321
+ self.eq(cm.exception.get('mesg'), 'No Nexus Pusher with iden newp.')
322
+
323
+ self.none(await cell00.nexsroot.nexslog.last())
324
+ self.none(await cell01.nexsroot.nexslog.last())
325
+
326
+ cell01.nexsiden = cell00.nexsiden
327
+ await cell01.sync()
328
+
329
+ self.eq(0, (await cell00.nexsroot.nexslog.last())[0])
330
+ self.eq(0, (await cell01.nexsroot.nexslog.last())[0])
331
+
332
+ with self.raises(s_exc.NoSuchName) as cm:
333
+ await cell01._push('newp')
334
+ self.eq(cm.exception.get('mesg'), 'No Nexus handler for event newp.')
335
+
336
+ self.eq(0, (await cell00.nexsroot.nexslog.last())[0])
337
+ self.eq(0, (await cell01.nexsroot.nexslog.last())[0])
338
+
313
339
  async def test_mirror_nexus_loop_failure(self):
314
340
  with self.getTestDir() as dirn:
315
341
 
@@ -1,5 +1,7 @@
1
1
  from unittest import mock
2
2
 
3
+ import regex
4
+
3
5
  import synapse.exc as s_exc
4
6
 
5
7
  import synapse.lib.scrape as s_scrape
@@ -889,14 +891,20 @@ class ScrapeTest(s_t_utils.SynTest):
889
891
  nodes.remove(('inet:url', 'smb://1:2:3:4:5:6:7:8/share'))
890
892
 
891
893
  async def test_scrape_async(self):
894
+ text = 'log4j vuln CVE-2021-44228 is pervasive'
895
+ ndefs = await s_t_utils.alist(s_scrape.scrapeAsync(text))
896
+ self.eq(ndefs, (('it:sec:cve', 'CVE-2021-44228'),))
892
897
 
893
- with mock.patch('synapse.lib.scrape.SCRAPE_SPAWN_LENGTH', 0):
894
- ndefs = await s_t_utils.alist(s_scrape.scrapeAsync('log4j vuln CVE-2021-44228 is pervasive'))
895
- self.eq(ndefs, (('it:sec:cve', 'CVE-2021-44228'),))
898
+ regx = regex.compile('(?P<valu>CVE-[0-9]{4}-[0-9]{4,})(?:[^a-z0-9]|$)')
899
+ infos = s_scrape._genMatchList(text, regx, {})
900
+ self.eq(infos, [{'match': 'CVE-2021-44228', 'offset': 11, 'valu': 'CVE-2021-44228'}])
896
901
 
897
- text = 'endashs are a vulnerability CVE\u20132022\u20131138 '
898
- infos = await s_t_utils.alist(s_scrape.contextScrapeAsync(text))
899
- self.eq(infos, [{'match': 'CVE–2022–1138', 'offset': 29, 'valu': 'CVE-2022-1138', 'form': 'it:sec:cve'}])
902
+ text = 'endashs are a vulnerability CVE\u20132022\u20131138 '
903
+ infos = await s_t_utils.alist(s_scrape.contextScrapeAsync(text))
904
+ self.eq(infos, [{'match': 'CVE–2022–1138', 'offset': 29, 'valu': 'CVE-2022-1138', 'form': 'it:sec:cve'}])
905
+
906
+ infos = s_scrape._contextScrapeList(text)
907
+ self.eq(infos, [{'match': 'CVE–2022–1138', 'offset': 29, 'valu': 'CVE-2022-1138', 'form': 'it:sec:cve'}])
900
908
 
901
909
  def test_scrape_sequential(self):
902
910
  md5 = ('a' * 32, 'b' * 32,)
@@ -638,6 +638,8 @@ class StormTest(s_t_utils.SynTest):
638
638
  with self.raises(s_exc.NoSuchVar):
639
639
  await core.nodes('background { $lib.print($foo) }')
640
640
 
641
+ await core.nodes('background ${ $foo=test $lib.print($foo) }')
642
+
641
643
  await core.nodes('background { $lib.time.sleep(4) }')
642
644
  task = await core.callStorm('for $t in $lib.ps.list() { if $t.info.background { return($t) } }')
643
645
  self.nn(task)
@@ -667,6 +669,9 @@ class StormTest(s_t_utils.SynTest):
667
669
  q = '[ inet:fqdn=www.vertex.link ] $q=:domain | parallel $q'
668
670
  await self.asyncraises(s_exc.StormRuntimeError, core.nodes(q))
669
671
 
672
+ nodes = await core.nodes('ou:org | parallel ${ $foo=bar [ :name=$foo ]}')
673
+ self.true(all([n.get('name') == 'bar' for n in nodes]))
674
+
670
675
  # test $lib.exit() and the StormExit handlers
671
676
  msgs = [m async for m in core.view.storm('$lib.exit()')]
672
677
  self.eq(msgs[-1][0], 'fini')
@@ -2137,6 +2142,49 @@ class StormTest(s_t_utils.SynTest):
2137
2142
  self.nn(bot.get('country::flag::md5'))
2138
2143
  self.eq(bot['country::flag::md5'][0], 'fa818a259cbed7ce8bc2a22d35a464fc')
2139
2144
 
2145
+ await core.nodes('''
2146
+ [( risk:vulnerable=*
2147
+ :mitigated=true
2148
+ :node={ [ it:prod:hardware=* :name=foohw ] return($node.ndef()) }
2149
+ :vuln={[ risk:vuln=* :name=barvuln ]}
2150
+ +#test
2151
+ )]
2152
+ [( inet:service:rule=*
2153
+ :object={ risk:vulnerable#test return($node.ndef()) }
2154
+ :grantee={ [ inet:service:account=* :id=foocon ] return($node.ndef()) }
2155
+ +#test
2156
+ )]
2157
+ ''')
2158
+
2159
+ opts = {
2160
+ 'embeds': {
2161
+ 'risk:vulnerable': {
2162
+ 'vuln': ['name'],
2163
+ 'node': ['name'],
2164
+ },
2165
+ 'inet:service:rule': {
2166
+ 'object': ['mitigated', 'newp'],
2167
+ 'object::node': ['name', 'newp'],
2168
+ 'grantee': ['id', 'newp'],
2169
+ }
2170
+ }
2171
+ }
2172
+ msgs = await core.stormlist('inet:service:rule#test :object -+> risk:vulnerable', opts=opts)
2173
+ nodes = sorted([m[1] for m in msgs if m[0] == 'node'], key=lambda p: p[0][0])
2174
+ self.eq(['inet:service:rule', 'risk:vulnerable'], [n[0][0] for n in nodes])
2175
+
2176
+ embeds = nodes[0][1]['embeds']
2177
+ self.eq(1, embeds['object']['mitigated'])
2178
+ self.eq(None, embeds['object']['newp'])
2179
+ self.eq('foohw', embeds['object::node']['name'])
2180
+ self.eq(None, embeds['object::node']['newp'])
2181
+ self.eq('foocon', embeds['grantee']['id'])
2182
+ self.eq(None, embeds['grantee']['newp'])
2183
+
2184
+ embeds = nodes[1][1]['embeds']
2185
+ self.eq('barvuln', embeds['vuln']['name'])
2186
+ self.eq('foohw', embeds['node']['name'])
2187
+
2140
2188
  async def test_storm_wget(self):
2141
2189
 
2142
2190
  async def _getRespFromSha(core, mesgs):