synapse 2.178.0__py311-none-any.whl → 2.179.0__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 (44) hide show
  1. synapse/cortex.py +162 -27
  2. synapse/datamodel.py +47 -1
  3. synapse/exc.py +1 -0
  4. synapse/lib/aha.py +2 -1
  5. synapse/lib/ast.py +26 -22
  6. synapse/lib/base.py +12 -3
  7. synapse/lib/cell.py +150 -11
  8. synapse/lib/coro.py +14 -0
  9. synapse/lib/drive.py +551 -0
  10. synapse/lib/schemas.py +39 -0
  11. synapse/lib/snap.py +17 -7
  12. synapse/lib/storm.py +3 -1
  13. synapse/lib/stormhttp.py +1 -0
  14. synapse/lib/stormlib/modelext.py +29 -3
  15. synapse/lib/stormlib/stix.py +40 -17
  16. synapse/lib/stormlib/vault.py +2 -2
  17. synapse/lib/stormtypes.py +1 -1
  18. synapse/lib/types.py +9 -0
  19. synapse/lib/version.py +2 -2
  20. synapse/lookup/pe.py +303 -38
  21. synapse/models/dns.py +24 -1
  22. synapse/models/geospace.py +4 -1
  23. synapse/models/infotech.py +26 -1
  24. synapse/tests/test_cortex.py +45 -1
  25. synapse/tests/test_lib_aha.py +17 -0
  26. synapse/tests/test_lib_cell.py +224 -0
  27. synapse/tests/test_lib_coro.py +12 -0
  28. synapse/tests/test_lib_stormhttp.py +40 -0
  29. synapse/tests/test_lib_stormlib_modelext.py +55 -3
  30. synapse/tests/test_lib_stormlib_stix.py +15 -0
  31. synapse/tests/test_lib_stormlib_vault.py +11 -1
  32. synapse/tests/test_lib_stormtypes.py +5 -0
  33. synapse/tests/test_lib_types.py +9 -0
  34. synapse/tests/test_model_dns.py +8 -0
  35. synapse/tests/test_model_geospace.py +3 -1
  36. synapse/tests/test_model_infotech.py +47 -0
  37. synapse/tests/test_model_syn.py +11 -0
  38. synapse/tests/test_utils_stormcov.py +1 -1
  39. synapse/tools/changelog.py +28 -0
  40. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/METADATA +1 -1
  41. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/RECORD +44 -43
  42. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/WHEEL +1 -1
  43. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/LICENSE +0 -0
  44. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,50 @@ class CortexTest(s_t_utils.SynTest):
41
41
  '''
42
42
  The tests that should be run with different types of layers
43
43
  '''
44
+ async def test_cortex_basics(self):
45
+
46
+ with self.getTestDir() as dirn:
47
+
48
+ async with self.getTestCore(dirn=dirn) as core:
49
+
50
+ with self.raises(s_exc.NoSuchProp):
51
+ await core.setPropLocked('newp', True)
52
+
53
+ with self.raises(s_exc.NoSuchUniv):
54
+ await core.setUnivLocked('newp', True)
55
+
56
+ with self.raises(s_exc.NoSuchTagProp):
57
+ await core.setTagPropLocked('newp', True)
58
+
59
+ await core.addTagProp('score', ('int', {}), {})
60
+
61
+ await core.setPropLocked('inet:ipv4:asn', True)
62
+ await core.setUnivLocked('.seen', True)
63
+ await core.setTagPropLocked('score', True)
64
+
65
+ with self.raises(s_exc.IsDeprLocked):
66
+ await core.nodes('[ inet:ipv4=1.2.3.4 :asn=99 ]')
67
+ with self.raises(s_exc.IsDeprLocked):
68
+ await core.nodes('[ inet:ipv4=1.2.3.4 .seen=now ]')
69
+ with self.raises(s_exc.IsDeprLocked):
70
+ await core.nodes('[ inet:ipv4=1.2.3.4 +#foo:score=10 ]')
71
+
72
+ # test persistence...
73
+ async with self.getTestCore(dirn=dirn) as core:
74
+
75
+ with self.raises(s_exc.IsDeprLocked):
76
+ await core.nodes('[ inet:ipv4=1.2.3.4 :asn=99 ]')
77
+ with self.raises(s_exc.IsDeprLocked):
78
+ await core.nodes('[ inet:ipv4=1.2.3.4 .seen=now ]')
79
+ with self.raises(s_exc.IsDeprLocked):
80
+ await core.nodes('[ inet:ipv4=1.2.3.4 +#foo:score=10 ]')
81
+
82
+ await core.setPropLocked('inet:ipv4:asn', False)
83
+ await core.setUnivLocked('.seen', False)
84
+ await core.setTagPropLocked('score', False)
85
+
86
+ await core.nodes('[ inet:ipv4=1.2.3.4 :asn=99 .seen=now +#foo:score=10 ]')
87
+
44
88
  async def test_cortex_cellguid(self):
45
89
  iden = s_common.guid()
46
90
  conf = {'cell:guid': iden}
@@ -1302,7 +1346,7 @@ class CortexTest(s_t_utils.SynTest):
1302
1346
 
1303
1347
  await core.delTagProp('score')
1304
1348
 
1305
- with self.raises(s_exc.NoSuchProp):
1349
+ with self.raises(s_exc.NoSuchTagProp):
1306
1350
  await core.delTagProp('score')
1307
1351
 
1308
1352
  modl = await core.getModelDict()
@@ -1292,3 +1292,20 @@ class AhaTest(s_test.SynTest):
1292
1292
  async with await s_cell.Cell.anit(dirn=clldir, conf=cllconf) as cell:
1293
1293
  self.none(await cell.ahaclient.waitready(timeout=12))
1294
1294
  self.eq(cell.conf.get('aha:registry'), ahaurl)
1295
+
1296
+ async def test_aha_provision_listen_dns_name(self):
1297
+ # Ensure that we use the dns:name for the provisioning listener when
1298
+ # the provision:listen value is not provided.
1299
+ conf = {
1300
+ 'aha:network': 'synapse',
1301
+ 'dns:name': 'here.loop.vertex.link',
1302
+ }
1303
+ mesg = 'provision listening: ssl://0.0.0.0:27272?hostname=here.loop.vertex.link'
1304
+ with self.getAsyncLoggerStream('synapse.lib.aha', mesg) as stream:
1305
+ async with self.getTestCell(s_aha.AhaCell, conf=conf) as aha:
1306
+ self.true(await stream.wait(timeout=6))
1307
+ # And the URL works with our listener :)
1308
+ provurl = await aha.addAhaUserEnroll('bob.grey')
1309
+ async with await s_telepath.openurl(provurl) as prox:
1310
+ info = await prox.getUserInfo()
1311
+ self.eq(info.get('aha:user'), 'bob.grey')
@@ -28,6 +28,7 @@ import synapse.lib.base as s_base
28
28
  import synapse.lib.cell as s_cell
29
29
  import synapse.lib.coro as s_coro
30
30
  import synapse.lib.link as s_link
31
+ import synapse.lib.drive as s_drive
31
32
  import synapse.lib.nexus as s_nexus
32
33
  import synapse.lib.certdir as s_certdir
33
34
  import synapse.lib.msgpack as s_msgpack
@@ -155,8 +156,217 @@ async def altAuthCtor(cell):
155
156
  cell.onfini(auth.fini)
156
157
  return auth
157
158
 
159
+ testDataSchema_v0 = {
160
+ 'type': 'object',
161
+ 'properties': {
162
+ 'type': {'type': 'string'},
163
+ 'size': {'type': 'number'},
164
+ },
165
+ 'required': ['type', 'size'],
166
+ 'additionalProperties': False,
167
+ }
168
+
169
+ testDataSchema_v1 = {
170
+ 'type': 'object',
171
+ 'properties': {
172
+ 'type': {'type': 'string'},
173
+ 'size': {'type': 'number'},
174
+ 'woot': {'type': 'string'},
175
+ },
176
+ 'required': ['type', 'size', 'woot'],
177
+ 'additionalProperties': False,
178
+ }
179
+
158
180
  class CellTest(s_t_utils.SynTest):
159
181
 
182
+ async def test_cell_drive(self):
183
+
184
+ async with self.getTestCell() as cell:
185
+
186
+ with self.raises(s_exc.BadName):
187
+ s_drive.reqValidName('A' * 512)
188
+
189
+ info = {'name': 'users'}
190
+ pathinfo = await cell.addDriveItem(info)
191
+
192
+ info = {'name': 'root'}
193
+ pathinfo = await cell.addDriveItem(info, path='users')
194
+
195
+ with self.raises(s_exc.DupIden):
196
+ await cell.drive.addItemInfo(pathinfo[-1], path='users')
197
+
198
+ rootdir = pathinfo[-1].get('iden')
199
+ self.eq(0, pathinfo[-1].get('kids'))
200
+
201
+ info = {'name': 'win32k.sys', 'type': 'hehe'}
202
+ with self.raises(s_exc.NoSuchType):
203
+ info = await cell.addDriveItem(info, reldir=rootdir)
204
+
205
+ infos = [i async for i in cell.getDriveKids(s_drive.rootdir)]
206
+ self.len(1, infos)
207
+ self.eq(1, infos[0].get('kids'))
208
+ self.eq('users', infos[0].get('name'))
209
+
210
+ # TODO how to handle iden match with additional property mismatch
211
+
212
+ await cell.drive.setTypeSchema('woot', testDataSchema_v0)
213
+
214
+ info = {'name': 'win32k.sys', 'type': 'woot'}
215
+ info = await cell.addDriveItem(info, reldir=rootdir)
216
+
217
+ iden = info[-1].get('iden')
218
+
219
+ tick = s_common.now()
220
+ rootuser = cell.auth.rootuser.iden
221
+
222
+ with self.raises(s_exc.SchemaViolation):
223
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
224
+ await cell.setDriveData(iden, versinfo, {'newp': 'newp'})
225
+
226
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
227
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 20})
228
+ self.eq(info.get('version'), (1, 1, 0))
229
+ self.eq(versinfo.get('version'), (1, 1, 0))
230
+
231
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
232
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
233
+ self.eq(info.get('version'), (1, 1, 0))
234
+ self.eq(versinfo.get('version'), (1, 0, 0))
235
+
236
+ versinfo10, data10 = await cell.getDriveData(iden, vers=(1, 0, 0))
237
+ self.eq(versinfo10.get('updated'), tick)
238
+ self.eq(versinfo10.get('updater'), rootuser)
239
+ self.eq(versinfo10.get('version'), (1, 0, 0))
240
+
241
+ versinfo11, data11 = await cell.getDriveData(iden, vers=(1, 1, 0))
242
+ self.eq(versinfo11.get('updated'), tick + 10)
243
+ self.eq(versinfo11.get('updater'), rootuser)
244
+ self.eq(versinfo11.get('version'), (1, 1, 0))
245
+
246
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
247
+ self.len(2, versions)
248
+ self.eq(versions[0], versinfo11)
249
+ self.eq(versions[1], versinfo10)
250
+
251
+ info = await cell.delDriveData(iden, vers=(0, 0, 0))
252
+
253
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
254
+ self.len(2, versions)
255
+ self.eq(versions[0], versinfo11)
256
+ self.eq(versions[1], versinfo10)
257
+
258
+ info = await cell.delDriveData(iden, vers=(1, 1, 0))
259
+ self.eq(info.get('updated'), tick)
260
+ self.eq(info.get('version'), (1, 0, 0))
261
+
262
+ info = await cell.delDriveData(iden, vers=(1, 0, 0))
263
+ self.eq(info.get('size'), 0)
264
+ self.eq(info.get('version'), (0, 0, 0))
265
+ self.none(info.get('updated'))
266
+ self.none(info.get('updater'))
267
+
268
+ # repopulate a couple data versions to test migration and delete
269
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
270
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
271
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
272
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17})
273
+ self.eq(versinfo, (await cell.getDriveData(iden))[0])
274
+
275
+ # This will be done by the cell in a cell storage version migration...
276
+ async def migrate_v1(info, versinfo, data):
277
+ data['woot'] = 'woot'
278
+ return data
279
+
280
+ await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
281
+
282
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
283
+ self.eq('woot', data.get('woot'))
284
+
285
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 1, 0))
286
+ self.eq('woot', data.get('woot'))
287
+
288
+ self.nn(await cell.getDriveInfo(iden))
289
+ self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
290
+
291
+ await cell.delDriveData(iden)
292
+ self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
293
+
294
+ await cell.delDriveInfo(iden)
295
+
296
+ self.none(await cell.getDriveInfo(iden))
297
+ self.len(0, [vers async for vers in cell.getDriveDataVersions(iden)])
298
+
299
+ with self.raises(s_exc.NoSuchPath):
300
+ await cell.getDrivePath('users/root/win32k.sys')
301
+
302
+ pathinfo = await cell.addDrivePath('foo/bar/baz')
303
+ self.len(3, pathinfo)
304
+ self.eq('foo', pathinfo[0].get('name'))
305
+ self.eq(1, pathinfo[0].get('kids'))
306
+ self.eq('bar', pathinfo[1].get('name'))
307
+ self.eq(1, pathinfo[1].get('kids'))
308
+ self.eq('baz', pathinfo[2].get('name'))
309
+ self.eq(0, pathinfo[2].get('kids'))
310
+
311
+ self.eq(pathinfo, await cell.addDrivePath('foo/bar/baz'))
312
+
313
+ baziden = pathinfo[2].get('iden')
314
+ self.eq(pathinfo, await cell.drive.getItemPath(baziden))
315
+
316
+ info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
317
+ self.eq(3, info['perm']['users'][rootuser])
318
+
319
+ with self.raises(s_exc.NoSuchIden):
320
+ # s_drive.rootdir is all 00s... ;)
321
+ await cell.setDriveInfoPerm(s_drive.rootdir, {'users': {}, 'roles': {}})
322
+
323
+ await cell.addDrivePath('hehe/haha')
324
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
325
+
326
+ self.eq('hoho', pathinfo[-1].get('name'))
327
+ self.eq(baziden, pathinfo[-1].get('iden'))
328
+
329
+ self.true(await cell.drive.hasPathInfo('hehe/haha/hoho'))
330
+ self.false(await cell.drive.hasPathInfo('foo/bar/baz'))
331
+
332
+ pathinfo = await cell.getDrivePath('foo/bar')
333
+ self.eq(0, pathinfo[-1].get('kids'))
334
+
335
+ pathinfo = await cell.getDrivePath('hehe/haha')
336
+ self.eq(1, pathinfo[-1].get('kids'))
337
+
338
+ with self.raises(s_exc.DupName):
339
+ iden = pathinfo[-2].get('iden')
340
+ name = pathinfo[-1].get('name')
341
+ cell.drive.reqFreeStep(iden, name)
342
+
343
+ walks = [item async for item in cell.drive.walkPathInfo('hehe')]
344
+ self.len(3, walks)
345
+ # confirm walked paths are yielded depth first...
346
+ self.eq('hoho', walks[0].get('name'))
347
+ self.eq('haha', walks[1].get('name'))
348
+ self.eq('hehe', walks[2].get('name'))
349
+
350
+ iden = walks[2].get('iden')
351
+ walks = [item async for item in cell.drive.walkItemInfo(iden)]
352
+ self.len(3, walks)
353
+ self.eq('hoho', walks[0].get('name'))
354
+ self.eq('haha', walks[1].get('name'))
355
+ self.eq('hehe', walks[2].get('name'))
356
+
357
+ self.none(cell.drive.getTypeSchema('newp'))
358
+
359
+ cell.drive.validators.pop('woot')
360
+ self.nn(cell.drive.getTypeValidator('woot'))
361
+
362
+ # move to root dir
363
+ pathinfo = await cell.setDriveInfoPath(baziden, 'zipzop')
364
+ self.len(1, pathinfo)
365
+ self.eq(s_drive.rootdir, pathinfo[-1].get('parent'))
366
+
367
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
368
+ self.len(3, pathinfo)
369
+
160
370
  async def test_cell_auth(self):
161
371
 
162
372
  with self.getTestDir() as dirn:
@@ -2914,3 +3124,17 @@ class CellTest(s_t_utils.SynTest):
2914
3124
 
2915
3125
  async with self.getTestCell(s_cell.Cell, dirn=dirn):
2916
3126
  pass
3127
+
3128
+ async def test_cell_initslab_fini(self):
3129
+ class SlabCell(s_cell.Cell):
3130
+ async def initServiceStorage(self):
3131
+ self.long_lived_slab = await self._initSlabFile(os.path.join(self.dirn, 'slabs', 'long.lmdb'))
3132
+ short_slab = await self._initSlabFile(os.path.join(self.dirn, 'slabs', 'short.lmdb'), ephemeral=True)
3133
+ self.short_slab_path = short_slab.lenv.path()
3134
+ await short_slab.fini()
3135
+
3136
+ async with self.getTestCell(SlabCell) as cell:
3137
+ self.true(os.path.isdir(cell.short_slab_path))
3138
+ self.isin(cell.long_lived_slab.fini, cell._fini_funcs)
3139
+ slabs = [s for s in cell.tofini if isinstance(s, s_lmdbslab.Slab) and s.lenv.path() == cell.short_slab_path]
3140
+ self.len(0, slabs)
@@ -41,6 +41,18 @@ def nopickle():
41
41
 
42
42
  class CoroTest(s_t_utils.SynTest):
43
43
 
44
+ async def test_coro_chunks(self):
45
+ async def agen():
46
+ for i in range(101):
47
+ yield i
48
+
49
+ chunks = []
50
+ async for chunk in s_coro.chunks(agen()):
51
+ chunks.append(chunk)
52
+
53
+ self.len(1, chunks[1])
54
+ self.len(100, chunks[0])
55
+
44
56
  async def test_coro_event(self):
45
57
 
46
58
  evnt = s_coro.Event()
@@ -692,6 +692,9 @@ class StormHttpTest(s_test.SynTest):
692
692
  tlscadir = s_common.gendir(dirn, 'cadir')
693
693
  cacertpath = shutil.copyfile(os.path.join(cadir, 'somelocalca.crt'), os.path.join(tlscadir, 'somelocalca.crt'))
694
694
 
695
+ with s_common.genfile(cacertpath) as fd:
696
+ ca_cert = fd.read().decode()
697
+
695
698
  pkey, cert = tdir.genUserCert('someuser', signas='somelocalca')
696
699
  user_pkey = tdir._pkeyToByts(pkey).decode()
697
700
  user_cert = tdir._certToByts(cert).decode()
@@ -835,3 +838,40 @@ class StormHttpTest(s_test.SynTest):
835
838
  ## bad cert
836
839
  sslopts['client_cert'] = 'not-gonna-work'
837
840
  await self.asyncraises(s_exc.BadArg, core.callStorm(q, opts=opts))
841
+
842
+ # Provide a CA certificate directly
843
+ async with self.getTestCore(dirn=dirn) as core:
844
+
845
+ sslctx = core.initSslCtx(certpath, pkeypath)
846
+ sslctx.load_verify_locations(cafile=cacertpath)
847
+
848
+ addr, port = await core.addHttpsPort(0, sslctx=sslctx)
849
+ root = await core.auth.getUserByName('root')
850
+ await root.setPasswd('root')
851
+
852
+ core.addHttpApi('/api/v0/test', s_test.HttpReflector, {'cell': core})
853
+
854
+ sslopts = {}
855
+
856
+ opts = {
857
+ 'vars': {
858
+ 'url': f'https://root:root@localhost:{port}/api/v0/test',
859
+ 'verify': True,
860
+ 'sslopts': sslopts,
861
+ },
862
+ }
863
+
864
+ q = 'return($lib.inet.http.get($url, ssl_verify=$verify, ssl_opts=$sslopts))'
865
+
866
+ size, sha256 = await core.callStorm('return($lib.bytes.put($lib.base64.decode(Zm9v)))')
867
+ opts['vars']['sha256'] = sha256
868
+
869
+ ## no cert provided
870
+ resp = await core.callStorm(q, opts=opts)
871
+ self.eq(-1, resp['code'])
872
+ self.isin('certificate verify failed', resp['reason'])
873
+
874
+ ## provide just the CA Certificate
875
+ sslopts['ca_cert'] = ca_cert
876
+ resp = await core.callStorm(q, opts=opts)
877
+ self.eq(200, resp['code'])
@@ -57,12 +57,24 @@ class StormtypesModelextTest(s_test.SynTest):
57
57
  model_defs = await core.callStorm('return ( $lib.model.ext.getExtModel() )')
58
58
  self.isinstance(model_defs, dict)
59
59
 
60
+ self.len(1, await core.nodes('_visi:int:tick'))
61
+ await core._delAllFormProp('_visi:int', 'tick', {})
62
+ self.len(0, await core.nodes('_visi:int:tick'))
63
+
64
+ self.len(1, await core.nodes('._woot'))
65
+ await core._delAllUnivProp('_woot', {})
66
+ self.len(0, await core.nodes('._woot'))
67
+
68
+ self.len(1, await core.nodes('#lol:score'))
69
+ await core._delAllTagProp('score', {})
70
+ self.len(0, await core.nodes('#lol:score'))
71
+
60
72
  await core.callStorm('_visi:int=10 test:int=1234 | delnode')
61
73
  await core.callStorm('''
62
- $lib.model.ext.delTagProp(score)
63
- $lib.model.ext.delUnivProp(_woot)
74
+ $lib.model.ext.delTagProp(score, force=(true))
75
+ $lib.model.ext.delUnivProp(_woot, force=(true))
64
76
  $lib.model.ext.delFormProp(_visi:int, tick)
65
- $lib.model.ext.delFormProp(test:int, _tick)
77
+ $lib.model.ext.delFormProp(test:int, _tick, force=(true))
66
78
  $lib.model.ext.delForm(_visi:int)
67
79
  $lib.model.ext.delEdge(inet:user, _copies, *)
68
80
  ''')
@@ -276,6 +288,46 @@ class StormtypesModelextTest(s_test.SynTest):
276
288
 
277
289
  self.nn(core.model.edge(('inet:user', '_copies', None)))
278
290
 
291
+ # Property values left behind in layers are cleanly removed
292
+ async with self.getTestCore() as core:
293
+ await core.callStorm('''
294
+ $typeinfo = ({})
295
+ $docinfo = ({"doc": "NEWP"})
296
+ $lib.model.ext.addUnivProp(_woot, (int, ({})), $docinfo)
297
+ $lib.model.ext.addTagProp(score, (int, ({})), $docinfo)
298
+ $lib.model.ext.addFormProp(test:int, _tick, (time, ({})), $docinfo)
299
+ ''')
300
+ fork = await core.callStorm('return ( $lib.view.get().fork().iden ) ')
301
+ nodes = await core.nodes('[test:int=1234 :_tick=2024 ._woot=1 +#hehe:score=10]')
302
+ self.len(1, nodes)
303
+ self.eq(nodes[0].get('._woot'), 1)
304
+
305
+ nodes = await core.nodes('test:int=1234 [:_tick=2023 ._woot=2 +#hehe:score=9]',
306
+ opts={'view': fork})
307
+ self.len(1, nodes)
308
+ self.eq(nodes[0].get('._woot'), 2)
309
+
310
+ self.len(0, await core.nodes('test:int | delnode'))
311
+
312
+ with self.raises(s_exc.CantDelUniv):
313
+ await core.callStorm('$lib.model.ext.delUnivProp(_woot)')
314
+ with self.raises(s_exc.CantDelProp):
315
+ await core.callStorm('$lib.model.ext.delFormProp(test:int, _tick)')
316
+ with self.raises(s_exc.CantDelProp):
317
+ await core.callStorm('$lib.model.ext.delTagProp(score)')
318
+
319
+ await core.callStorm('$lib.model.ext.delUnivProp(_woot, force=(true))')
320
+ await core.callStorm('$lib.model.ext.delFormProp(test:int, _tick, force=(true))')
321
+ await core.callStorm('$lib.model.ext.delTagProp(score, force=(true))')
322
+
323
+ nodes = await core.nodes('[test:int=1234]')
324
+ self.len(1, nodes)
325
+ self.none(nodes[0].get('._woot'))
326
+ self.none(nodes[0].get('_tick'))
327
+ nodes = await core.nodes('test:int=1234', opts={'view': fork})
328
+ self.none(nodes[0].get('._woot'))
329
+ self.none(nodes[0].get('_tick'))
330
+
279
331
  async def test_lib_stormlib_behold_modelext(self):
280
332
  self.skipIfNexusReplay()
281
333
  async with self.getTestCore() as core:
@@ -410,6 +410,21 @@ class StormLibStixTest(s_test.SynTest):
410
410
  self.len(1, [n for n in nodes if n[0][0] == 'it:cmd'])
411
411
  self.stormIsInWarn("STIX bundle ingest has no relationship definition for: ('threat-actor', 'gronks', 'threat-actor')", msgs)
412
412
 
413
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), newp)')
414
+ self.stormIsInErr('config must be a dictionary', msgs)
415
+
416
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"relationships": 5}))')
417
+ self.stormIsInErr('Error processing relationships', msgs)
418
+
419
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"bundle": 3}))')
420
+ self.stormIsInErr('bundle value must be a dictionary', msgs)
421
+
422
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({}), ({"bundle": {"storm": 3}}))')
423
+ self.stormIsInErr('storm query must be a string', msgs)
424
+
425
+ msgs = await core.stormlist('yield $lib.stix.import.ingest(({"objects": 3}), ({}))')
426
+ self.stormIsInErr('Error processing objects', msgs)
427
+
413
428
  async def test_stix_export_custom(self):
414
429
 
415
430
  async with self.getTestCore() as core:
@@ -159,10 +159,13 @@ class StormlibVaultTest(s_test.SynTest):
159
159
 
160
160
  # Rename vault
161
161
  opts = {'vars': {'giden': giden}}
162
+ self.eq('gvault', await core.callStorm('return($lib.vault.get($giden).name)', opts=opts))
162
163
  q = '$lib.vault.get($giden).name = foobar'
163
164
  await core.callStorm(q, opts=opts)
164
165
  vault = core.getVault(giden)
165
166
  self.eq(vault.get('name'), 'foobar')
167
+ self.nn(await core.callStorm('return($lib.vault.byname(foobar))'))
168
+ await self.asyncraises(s_exc.NoSuchName, core.callStorm('return($lib.vault.byname(gvault))'))
166
169
 
167
170
  # Get secrets without EDIT perms
168
171
  opts = {'vars': {'giden': giden}, 'user': visi1.iden}
@@ -222,10 +225,17 @@ class StormlibVaultTest(s_test.SynTest):
222
225
  q = '$vault = $lib.vault.get($giden) return($vault.setPerm($iden, $lib.auth.easyperm.level.deny))'
223
226
  self.true(await core.callStorm(q, opts=opts))
224
227
 
225
- # List vaults again
226
228
  opts = {'user': visi1.iden}
227
229
  self.eq(0, await core.callStorm('return($lib.len($lib.vault.list()))', opts=opts))
228
230
 
231
+ # Remove permission on global vault
232
+ opts = {'vars': {'iden': visi1.iden, 'giden': giden}}
233
+ q = '$vault = $lib.vault.get($giden) return($vault.setPerm($iden, $lib.null))'
234
+ self.true(await core.callStorm(q, opts=opts))
235
+
236
+ opts = {'user': visi1.iden}
237
+ self.eq(1, await core.callStorm('return($lib.len($lib.vault.list()))', opts=opts))
238
+
229
239
  # Runtime asroot
230
240
 
231
241
  await core.addStormPkg({
@@ -6388,6 +6388,11 @@ words\tword\twrd'''
6388
6388
  self.eq(1, await core.callStorm('return($lib.math.number(1.23).toint())'))
6389
6389
  self.eq(2, await core.callStorm('return($lib.math.number(1.23).toint(rounding=ROUND_UP))'))
6390
6390
 
6391
+ with self.raises(s_exc.BadCast):
6392
+ await core.callStorm('return($lib.math.number((null)))')
6393
+ with self.raises(s_exc.BadCast):
6394
+ await core.callStorm('return($lib.math.number(newp))')
6395
+
6391
6396
  with self.raises(s_exc.StormRuntimeError):
6392
6397
  await core.callStorm('return($lib.math.number(1.23).toint(rounding=NEWP))')
6393
6398
 
@@ -1606,3 +1606,12 @@ class TypesTest(s_t_utils.SynTest):
1606
1606
 
1607
1607
  core.getLayer()._testAddPropArrayIndx(buid, 'test:int', '_hehe', ('newp' * 100,))
1608
1608
  self.len(0, await core.nodes('test:int:_hehe*[~=newp]'))
1609
+
1610
+ async def test_types_typehash(self):
1611
+ async with self.getTestCore() as core:
1612
+ self.true(core.model.form('inet:fqdn').type.typehash is core.model.prop('inet:dns:a:fqdn').type.typehash)
1613
+ self.true(core.model.form('it:prod:softname').type.typehash is core.model.prop('it:network:name').type.typehash)
1614
+ self.true(core.model.form('inet:asn').type.typehash is not core.model.prop('inet:proto:port').type.typehash)
1615
+
1616
+ self.true(s_common.isguid(core.model.form('inet:fqdn').type.typehash))
1617
+ self.true(s_common.isguid(core.model.form('inet:fqdn').typehash))
@@ -108,6 +108,14 @@ class DnsModelTest(s_t_utils.SynTest):
108
108
  self.eq(node.get('name:fqdn'), 'vertex.link')
109
109
  self.eq(node.get('type'), 255)
110
110
 
111
+ nodes = await core.nodes('[inet:dns:request=* :reply:code=NXDOMAIN]')
112
+ self.eq(nodes[0].get('reply:code'), 3)
113
+ self.eq(nodes[0].repr('reply:code'), 'NXDOMAIN')
114
+
115
+ nodes = await core.nodes('[inet:dns:request=* :reply:code=1138]')
116
+ self.eq(nodes[0].get('reply:code'), 1138)
117
+ self.eq(nodes[0].repr('reply:code'), '1138')
118
+
111
119
  nodes = await core.nodes('[inet:dns:query=("tcp://1.2.3.4", 4.3.2.1.in-addr.arpa, 255)]')
112
120
  self.len(1, nodes)
113
121
  node = nodes[0]
@@ -2,7 +2,6 @@ import synapse.exc as s_exc
2
2
  import synapse.common as s_common
3
3
 
4
4
  import synapse.tests.utils as s_t_utils
5
- from synapse.tests.utils import alist
6
5
 
7
6
  import synapse.lib.module as s_module
8
7
 
@@ -499,6 +498,7 @@ class GeoTest(s_t_utils.SynTest):
499
498
  :place:name=woot
500
499
  :place={[geo:place=* :name=woot]}
501
500
  :accuracy=10m
501
+ :node=(test:int, 1234)
502
502
  ]
503
503
  ''')
504
504
  self.eq(1655510400000, nodes[0].get('time'))
@@ -507,6 +507,8 @@ class GeoTest(s_t_utils.SynTest):
507
507
  self.eq('woot', nodes[0].get('place:name'))
508
508
  self.eq(10000, nodes[0].get('accuracy'))
509
509
  self.len(1, await core.nodes('geo:telem -> geo:place +:name=woot'))
510
+ self.eq(('test:int', 1234), nodes[0].get('node'))
511
+ self.len(1, await core.nodes('test:int=1234'))
510
512
 
511
513
  async def test_model_geospace_area(self):
512
514
 
@@ -407,6 +407,35 @@ class InfotechModelTest(s_t_utils.SynTest):
407
407
  self.eq(nodes[0].get('net6'), ('fe80::', 'fe80::ffff:ffff:ffff:ffff'))
408
408
  self.eq(nodes[0].get('type'), 'virtual.sdn.')
409
409
 
410
+ nodes = await core.nodes('''[
411
+ it:sec:stix:indicator=*
412
+ :id=zoinks
413
+ :name=woot
414
+ :confidence=90
415
+ :revoked=(false)
416
+ :description="my neato indicator"
417
+ :pattern="some rule text"
418
+ :pattern_type=yara
419
+ :created=20240815
420
+ :updated=20240815
421
+ :labels=(hehe, haha)
422
+ :valid_from=20240815
423
+ :valid_until=20240815
424
+ ]''')
425
+ self.len(1, nodes)
426
+ self.eq('zoinks', nodes[0].get('id'))
427
+ self.eq('woot', nodes[0].get('name'))
428
+ self.eq(90, nodes[0].get('confidence'))
429
+ self.eq(False, nodes[0].get('revoked'))
430
+ self.eq('my neato indicator', nodes[0].get('description'))
431
+ self.eq('some rule text', nodes[0].get('pattern'))
432
+ self.eq('yara', nodes[0].get('pattern_type'))
433
+ self.eq(('haha', 'hehe'), nodes[0].get('labels'))
434
+ self.eq(1723680000000, nodes[0].get('created'))
435
+ self.eq(1723680000000, nodes[0].get('updated'))
436
+ self.eq(1723680000000, nodes[0].get('valid_from'))
437
+ self.eq(1723680000000, nodes[0].get('valid_until'))
438
+
410
439
  async def test_infotech_ios(self):
411
440
 
412
441
  async with self.getTestCore() as core:
@@ -1553,6 +1582,24 @@ class InfotechModelTest(s_t_utils.SynTest):
1553
1582
  self.eq(rule, nodes[0].get('rule'))
1554
1583
  self.eq(0x10000200003, nodes[0].get('version'))
1555
1584
 
1585
+ nodes = await core.nodes('''[
1586
+ (it:app:yara:netmatch=* :node=(inet:fqdn, foo.com))
1587
+ (it:app:yara:netmatch=* :node=(inet:ipv4, 1.2.3.4))
1588
+ (it:app:yara:netmatch=* :node=(inet:ipv6, "::ffff"))
1589
+ (it:app:yara:netmatch=* :node=(inet:url, "http://foo.com"))
1590
+ :rule=$rule
1591
+ :version=1.2.3
1592
+ ]''', opts=opts)
1593
+ self.len(4, nodes)
1594
+ for node in nodes:
1595
+ self.nn(node.get('node'))
1596
+ self.nn(node.get('version'))
1597
+
1598
+ self.len(4, await core.nodes('it:app:yara:rule=$rule -> it:app:yara:netmatch', opts=opts))
1599
+
1600
+ with self.raises(s_exc.BadTypeValu):
1601
+ await core.nodes('[it:app:yara:netmatch=* :node=(it:dev:str, foo)]')
1602
+
1556
1603
  async def test_it_app_snort(self):
1557
1604
 
1558
1605
  async with self.getTestCore() as core: