synapse 2.169.0__py311-none-any.whl → 2.171.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 (41) hide show
  1. synapse/cortex.py +99 -3
  2. synapse/datamodel.py +5 -0
  3. synapse/lib/ast.py +70 -12
  4. synapse/lib/cell.py +76 -7
  5. synapse/lib/layer.py +75 -6
  6. synapse/lib/lmdbslab.py +17 -0
  7. synapse/lib/node.py +7 -0
  8. synapse/lib/snap.py +22 -4
  9. synapse/lib/storm.py +1 -1
  10. synapse/lib/stormlib/cortex.py +1 -1
  11. synapse/lib/stormlib/model.py +339 -40
  12. synapse/lib/stormtypes.py +58 -1
  13. synapse/lib/types.py +36 -1
  14. synapse/lib/version.py +2 -2
  15. synapse/lib/view.py +94 -15
  16. synapse/models/files.py +40 -0
  17. synapse/models/inet.py +8 -4
  18. synapse/models/infotech.py +355 -17
  19. synapse/tests/files/cpedata.json +525034 -0
  20. synapse/tests/test_cortex.py +108 -0
  21. synapse/tests/test_lib_ast.py +66 -0
  22. synapse/tests/test_lib_cell.py +112 -0
  23. synapse/tests/test_lib_layer.py +52 -1
  24. synapse/tests/test_lib_lmdbslab.py +36 -0
  25. synapse/tests/test_lib_scrape.py +72 -71
  26. synapse/tests/test_lib_snap.py +16 -1
  27. synapse/tests/test_lib_storm.py +118 -0
  28. synapse/tests/test_lib_stormlib_cortex.py +15 -0
  29. synapse/tests/test_lib_stormlib_model.py +427 -0
  30. synapse/tests/test_lib_stormtypes.py +147 -15
  31. synapse/tests/test_lib_types.py +21 -0
  32. synapse/tests/test_lib_view.py +77 -0
  33. synapse/tests/test_model_files.py +52 -0
  34. synapse/tests/test_model_inet.py +63 -1
  35. synapse/tests/test_model_infotech.py +187 -26
  36. synapse/tests/utils.py +42 -9
  37. {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/METADATA +1 -1
  38. {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/RECORD +41 -40
  39. {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/LICENSE +0 -0
  40. {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/WHEEL +0 -0
  41. {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/top_level.txt +0 -0
@@ -2430,6 +2430,41 @@ class CortexTest(s_t_utils.SynTest):
2430
2430
  msgs = await core.stormlist('test:int :loc -> test:newp')
2431
2431
  self.stormIsInErr('No property named test:newp', msgs)
2432
2432
 
2433
+ # ndef pivots
2434
+ await core.nodes('''
2435
+ [
2436
+ ( test:str=ndefpivdst )
2437
+ ( test:str=ndefpivsrc :bar=(test:str, ndefpivdst) )
2438
+ ( test:str=ndefpivprp :bar=(test:str, ndefpivdst) )
2439
+ ]
2440
+ ''')
2441
+
2442
+ nodes = await core.nodes('test:str=ndefpivsrc -> test:str')
2443
+ self.eq(['ndefpivdst'], [n.ndef[1] for n in nodes])
2444
+
2445
+ nodes = await core.nodes('test:str=ndefpivsrc -> test:str:bar')
2446
+ self.len(0, nodes)
2447
+
2448
+ nodes = await core.nodes('test:str=ndefpivdst -> test:str:bar')
2449
+ self.sorteq(['ndefpivprp', 'ndefpivsrc'], [n.ndef[1] for n in nodes])
2450
+
2451
+ nodes = await core.nodes('test:str=ndefpivsrc :bar -> * +test:str')
2452
+ self.eq(['ndefpivdst'], [n.ndef[1] for n in nodes])
2453
+
2454
+ nodes = await core.nodes('test:str=ndefpivsrc :bar -> test:str:bar')
2455
+ self.sorteq(['ndefpivprp', 'ndefpivsrc'], [n.ndef[1] for n in nodes])
2456
+
2457
+ nodes = await core.nodes('test:str=ndefpivsrc :bar -> test:str')
2458
+ self.eq(['ndefpivdst'], [n.ndef[1] for n in nodes])
2459
+
2460
+ nodes = await core.nodes('test:str=ndefpivsrc :bar -> test:int')
2461
+ self.len(0, nodes)
2462
+
2463
+ await core.nodes('test:str=ndefpivdst delnode')
2464
+ msgs = await core.stormlist('test:str=ndefpivsrc :bar -> test:str')
2465
+ self.len(0, [m for m in msgs if m[0] == 'node'])
2466
+ self.stormIsInWarn("Missing node corresponding to ndef ('test:str', 'ndefpivdst')", msgs)
2467
+
2433
2468
  # Bad pivot syntax go here
2434
2469
  for q in ['test:pivcomp :lulz <- *',
2435
2470
  'test:pivcomp :lulz <+- *',
@@ -6257,12 +6292,14 @@ class CortexBasicTest(s_t_utils.SynTest):
6257
6292
 
6258
6293
  # Can't delete the default view
6259
6294
  await self.asyncraises(s_exc.SynErr, core.delView(core.view.iden))
6295
+ await self.asyncraises(s_exc.SynErr, core._delViewWithLayer(core.view.iden, None, None))
6260
6296
 
6261
6297
  # Can't delete a layer in a view
6262
6298
  await self.asyncraises(s_exc.SynErr, core.delLayer(core.view.layers[0].iden))
6263
6299
 
6264
6300
  # Can't delete a nonexistent view
6265
6301
  await self.asyncraises(s_exc.NoSuchView, core.delView('XXX'))
6302
+ await self.asyncraises(s_exc.NoSuchView, core.delViewWithLayer('XXX'))
6266
6303
 
6267
6304
  # Can't delete a nonexistent layer
6268
6305
  await self.asyncraises(s_exc.NoSuchLayer, core.delLayer('XXX'))
@@ -6275,6 +6312,77 @@ class CortexBasicTest(s_t_utils.SynTest):
6275
6312
  await core.delView(view2_iden)
6276
6313
  await self.asyncraises(s_exc.NoSuchView, core.delView(view2_iden))
6277
6314
 
6315
+ layr = await core.addLayer()
6316
+ layriden = layr['iden']
6317
+ vdef3 = {'layers': (layriden,)}
6318
+ view3_iden = (await core.addView(vdef3)).get('iden')
6319
+
6320
+ opts = {'view': view3_iden}
6321
+ await core.callStorm('$lib.view.get().set(protected, $lib.true)', opts=opts)
6322
+
6323
+ await self.asyncraises(s_exc.CantDelView, core.delViewWithLayer(view3_iden))
6324
+
6325
+ await core.callStorm('$lib.view.get().set(protected, $lib.false)', opts=opts)
6326
+
6327
+ view3 = core.getView(view3_iden)
6328
+ vdef4 = await view3.fork()
6329
+
6330
+ deadlayr = view3.layers[0].iden
6331
+ view4_iden = vdef4.get('iden')
6332
+ view4 = core.getView(view4_iden)
6333
+
6334
+ self.eq(view4.parent, view3)
6335
+ self.len(2, view4.layers)
6336
+
6337
+ await core.auth.rootuser.setPasswd('secret')
6338
+ host, port = await core.dmon.listen('tcp://127.0.0.1:0/')
6339
+ layr2 = await core.callStorm('$layer=$lib.layer.add() return($layer)')
6340
+ varz = {'iden': layriden, 'tgt': layr2.get('iden'), 'port': port}
6341
+ opts = {'vars': varz, 'view': view3_iden}
6342
+
6343
+ pullq = '$layer=$lib.layer.get($iden).addPull(`tcp://root:secret@127.0.0.1:{$port}/*/layer/{$tgt}`)'
6344
+ pushq = '$layer=$lib.layer.get($iden).addPush(`tcp://root:secret@127.0.0.1:{$port}/*/layer/{$tgt}`)'
6345
+ msgs = await core.stormlist(pullq, opts=opts)
6346
+ self.stormHasNoWarnErr(msgs)
6347
+
6348
+ msgs = await core.stormlist(pushq, opts=opts)
6349
+ self.stormHasNoWarnErr(msgs)
6350
+
6351
+ coros = len(core.activecoros)
6352
+
6353
+ layridens = [lyr.iden for lyr in view4.layers if lyr.iden != view3.layers[0].iden]
6354
+ events = [
6355
+ {'event': 'view:setlayers', 'info': {'iden': view4.iden, 'layers': layridens}},
6356
+ {'event': 'view:set', 'info': {'iden': view4.iden, 'name': 'parent', 'valu': None}}
6357
+ ]
6358
+ task = core.schedCoro(s_t_utils.waitForBehold(core, events))
6359
+
6360
+ await core.delViewWithLayer(view3_iden)
6361
+
6362
+ await asyncio.wait_for(task, timeout=1)
6363
+
6364
+ # push/pull activecoros have been deleted
6365
+ self.len(coros - 2, core.activecoros)
6366
+
6367
+ self.none(view4.parent)
6368
+ self.len(1, view4.layers)
6369
+ self.none(core.getLayer(deadlayr))
6370
+
6371
+ vdef5 = await view4.fork()
6372
+ view5 = core.getView(vdef5.get('iden'))
6373
+
6374
+ usedlayr = view4.layers[0].iden
6375
+ vdef6 = {'layers': (usedlayr,)}
6376
+ view6 = core.getView((await core.addView(vdef6)).get('iden'))
6377
+
6378
+ await core.delViewWithLayer(view4_iden)
6379
+
6380
+ self.none(view5.parent)
6381
+ self.len(1, view5.layers)
6382
+
6383
+ self.nn(core.getLayer(usedlayr))
6384
+ self.eq([usedlayr], [lyr.iden for lyr in view6.layers])
6385
+
6278
6386
  async def test_cortex_view_opts(self):
6279
6387
  '''
6280
6388
  Test that the view opts work
@@ -670,6 +670,28 @@ class AstTest(s_test.SynTest):
670
670
  self.len(1, nodes)
671
671
  self.eq('geo:nloc', nodes[0].ndef[0])
672
672
 
673
+ await core.nodes('[ test:str=ndefs :ndefs=((it:dev:int, 1), (it:dev:int, 2)) ]')
674
+ await core.nodes('test:str=ndefs [ :ndefs += (inet:fqdn, woot.com) ]')
675
+ self.len(1, nodes)
676
+
677
+ nodes = await core.nodes('it:dev:int=1 -> test:str:ndefs')
678
+ self.len(1, nodes)
679
+ self.eq('ndefs', nodes[0].ndef[1])
680
+ self.eq(nodes[0].getNodeRefs(), [
681
+ ('ndefs', ('it:dev:int', 1)),
682
+ ('ndefs', ('it:dev:int', 2)),
683
+ ('ndefs', ('inet:fqdn', 'woot.com'))
684
+ ])
685
+
686
+ nodes = await core.nodes('[ test:str = norefs ]')
687
+ self.eq(nodes[0].getNodeRefs(), [])
688
+
689
+ self.len(1, await core.nodes('it:dev:int=1 -> test:str'))
690
+ self.len(3, await core.nodes('test:str=ndefs -> *'))
691
+ self.len(2, await core.nodes('test:str=ndefs -> it:dev:int'))
692
+ self.len(3, await core.nodes('test:str=ndefs :ndefs -> *'))
693
+ self.len(2, await core.nodes('test:str=ndefs :ndefs -> it:dev:int'))
694
+
673
695
  async def test_ast_pivot(self):
674
696
  # a general purpose pivot test. come on in!
675
697
  async with self.getTestCore() as core:
@@ -2761,6 +2783,50 @@ class AstTest(s_test.SynTest):
2761
2783
  self.stormHasNoWarnErr(msgs)
2762
2784
  self.len(0, calls)
2763
2785
 
2786
+ async def test_ast_tag_optimization(self):
2787
+ calls = []
2788
+ origtag = s_snap.Snap.nodesByTag
2789
+
2790
+ async def checkTag(self, tag, form=None, reverse=False):
2791
+ calls.append(('tag', tag, form))
2792
+ async for node in origtag(self, tag, form=form, reverse=reverse):
2793
+ yield node
2794
+
2795
+ with mock.patch('synapse.lib.snap.Snap.nodesByTag', checkTag):
2796
+ async with self.getTestCore() as core:
2797
+ self.len(1, await core.nodes('[inet:asn=200 :name=visi]'))
2798
+ self.len(1, await core.nodes('[test:int=12 +#visi]'))
2799
+ self.len(1, await core.nodes('[test:int=99 +#visi]'))
2800
+
2801
+ nodes = await core.nodes('test:int +#$x', opts={'vars': {'x': 'visi'}})
2802
+ self.len(2, nodes)
2803
+ self.len(1, calls)
2804
+ self.eq(('tag', 'visi', 'test:int'), calls[0])
2805
+
2806
+ calls = []
2807
+ # not for non-runtsafe
2808
+ nodes = await core.nodes('inet:asn:name $valu=:name test:int +#$valu')
2809
+ self.len(2, nodes)
2810
+ self.len(0, calls)
2811
+
2812
+ nodes = await core.nodes('''
2813
+ $tag = 'yeyeyeyeyeyeyeyeye'
2814
+ if $x {
2815
+ $tag = 'visi'
2816
+ } else {
2817
+ $tag = 'lolnope'
2818
+ }
2819
+ test:int +#$tag
2820
+ ''', opts={'vars': {'x': True}})
2821
+ self.len(2, nodes)
2822
+ self.len(1, calls)
2823
+ self.eq(('tag', 'visi', 'test:int'), calls[0])
2824
+
2825
+ calls = []
2826
+ nodes = await core.nodes('test:int +#$x', opts={'vars': {'x': 'v*'}})
2827
+ self.len(2, nodes)
2828
+ self.len(0, calls)
2829
+
2764
2830
  async def test_ast_cmdoper(self):
2765
2831
 
2766
2832
  async with self.getTestCore() as core:
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import ssl
3
3
  import sys
4
+ import json
4
5
  import time
5
6
  import base64
6
7
  import signal
@@ -33,6 +34,7 @@ import synapse.lib.version as s_version
33
34
  import synapse.lib.hiveauth as s_hiveauth
34
35
  import synapse.lib.lmdbslab as s_lmdbslab
35
36
  import synapse.lib.crypto.passwd as s_passwd
37
+ import synapse.lib.platforms.linux as s_linux
36
38
 
37
39
  import synapse.tools.backup as s_tools_backup
38
40
 
@@ -2611,3 +2613,113 @@ class CellTest(s_t_utils.SynTest):
2611
2613
 
2612
2614
  with self.raises(s_exc.NoSuchIden):
2613
2615
  await cell.delUserApiKey(newp)
2616
+
2617
+ async def test_cell_check_sysctl(self):
2618
+ sysctls = s_linux.getSysctls()
2619
+
2620
+ sysvals = s_cell.Cell.SYSCTL_VALS.copy()
2621
+ sysvals['vm.dirty_expire_centisecs'] += 1
2622
+ sysvals['vm.dirty_writeback_centisecs'] += 1
2623
+
2624
+ # Detect and report incorrect values
2625
+ with self.getStructuredAsyncLoggerStream('synapse.lib.cell') as stream:
2626
+ with mock.patch.object(s_cell.Cell, 'SYSCTL_VALS', sysvals):
2627
+ async with self.getTestCore(conf={'health:sysctl:checks': True}):
2628
+ pass
2629
+
2630
+ stream.seek(0)
2631
+ data = stream.getvalue()
2632
+ raw_mesgs = [m for m in data.split('\\n') if m]
2633
+ msgs = [json.loads(m) for m in raw_mesgs]
2634
+
2635
+ self.len(1, msgs)
2636
+
2637
+ mesg = f'Sysctl values different than expected: {", ".join(sysvals)}. '
2638
+ mesg += 'See https://synapse.docs.vertex.link/en/latest/synapse/devopsguide.html#performance-tuning '
2639
+ mesg += 'for information about these sysctl parameters.'
2640
+ self.eq(msgs[0]['message'], mesg)
2641
+ self.eq(msgs[0]['sysctls'], [
2642
+ {'name': 'vm.dirty_expire_centisecs', 'expected': 21, 'actual': sysctls['vm.dirty_expire_centisecs']},
2643
+ {'name': 'vm.dirty_writeback_centisecs', 'expected': 21, 'actual': sysctls['vm.dirty_writeback_centisecs']},
2644
+ ])
2645
+
2646
+ # Copy the current sysctl valus to the cell so the check passes
2647
+ sysvals = {
2648
+ 'vm.dirty_expire_centisecs': sysctls['vm.dirty_expire_centisecs'],
2649
+ 'vm.dirty_writeback_centisecs': sysctls['vm.dirty_writeback_centisecs'],
2650
+ }
2651
+
2652
+ # Detect correct values and stop the task
2653
+ with self.getLoggerStream('synapse.lib.cell') as stream:
2654
+ with mock.patch.object(s_cell.Cell, 'SYSCTL_VALS', sysvals):
2655
+ async with self.getTestCore():
2656
+ pass
2657
+
2658
+ stream.seek(0)
2659
+ data = stream.read()
2660
+ self.len(0, data)
2661
+
2662
+ # Disable the sysctl check and don't check at all
2663
+ with self.getLoggerStream('synapse.lib.cell') as stream:
2664
+ conf = {'health:sysctl:checks': False}
2665
+ async with self.getTestCore(conf=conf):
2666
+ pass
2667
+
2668
+ stream.seek(0)
2669
+ data = stream.read()
2670
+ self.len(0, data, msg=data)
2671
+
2672
+ async def test_cell_version_regression(self):
2673
+ oldver = (0, 1, 0)
2674
+ newver = (0, 2, 0)
2675
+
2676
+ class TestCell(s_cell.Cell):
2677
+ VERSION = newver
2678
+
2679
+ with self.getTestDir() as dirn:
2680
+ async with self.getTestCell(TestCell, dirn=dirn):
2681
+ pass
2682
+
2683
+ with self.raises(s_exc.BadVersion) as exc:
2684
+ with mock.patch.object(TestCell, 'VERSION', oldver):
2685
+ with self.getLoggerStream('synapse.lib.cell') as stream:
2686
+ async with self.getTestCell(TestCell, dirn=dirn):
2687
+ pass
2688
+
2689
+ mesg = f'Cell version regression (testcell) is not allowed! Stored version: {newver}, current version: {oldver}.'
2690
+ self.eq(exc.exception.get('mesg'), mesg)
2691
+ self.eq(exc.exception.get('currver'), oldver)
2692
+ self.eq(exc.exception.get('lastver'), newver)
2693
+
2694
+ stream.seek(0)
2695
+ data = stream.read()
2696
+ self.isin(mesg, data)
2697
+
2698
+ async with self.getTestCell(TestCell, dirn=dirn):
2699
+ pass
2700
+
2701
+ with self.getTestDir() as dirn:
2702
+ async with self.getTestCell(s_cell.Cell, dirn=dirn):
2703
+ pass
2704
+
2705
+ synver = list(s_version.version)
2706
+ synver[1] -= 1
2707
+ synver = tuple(synver)
2708
+
2709
+ with self.raises(s_exc.BadVersion) as exc:
2710
+ with mock.patch.object(s_version, 'version', synver):
2711
+ with self.getLoggerStream('synapse.lib.cell') as stream:
2712
+ async with self.getTestCell(s_cell.Cell, dirn=dirn):
2713
+ pass
2714
+
2715
+ mesg = f'Synapse version regression (cell) is not allowed! Stored version: {s_version.version}, current version: {synver}.'
2716
+ self.eq(exc.exception.get('mesg'), mesg)
2717
+ self.eq(exc.exception.get('currver'), synver)
2718
+ self.eq(exc.exception.get('lastver'), s_version.version)
2719
+
2720
+ stream.seek(0)
2721
+ data = stream.read()
2722
+ self.isin(mesg, data)
2723
+
2724
+ async with self.getTestCell(s_cell.Cell, dirn=dirn):
2725
+ pass
@@ -26,7 +26,7 @@ class LayerTest(s_t_utils.SynTest):
26
26
 
27
27
  def checkLayrvers(self, core):
28
28
  for layr in core.layers.values():
29
- self.eq(layr.layrvers, 10)
29
+ self.eq(layr.layrvers, 11)
30
30
 
31
31
  async def test_layer_verify(self):
32
32
 
@@ -1530,6 +1530,57 @@ class LayerTest(s_t_utils.SynTest):
1530
1530
  verbs = [verb async for verb in nodes0[0].iterEdgeVerbs(buid2)]
1531
1531
  self.len(0, verbs)
1532
1532
 
1533
+ async def test_layer_v11(self):
1534
+
1535
+ try:
1536
+
1537
+ oldv = s_layer.MIGR_COMMIT_SIZE
1538
+ s_layer.MIGR_COMMIT_SIZE = 1
1539
+
1540
+ async with self.getRegrCore('layer-v11') as core:
1541
+
1542
+ wlyrs_byview = await core.callStorm('''
1543
+ $wlyrs = ({})
1544
+ for $view in $lib.view.list() {
1545
+ $wlyrs.($view.get(name)) = $view.layers.0.iden
1546
+ }
1547
+ return($wlyrs)
1548
+ ''')
1549
+ self.len(8, wlyrs_byview)
1550
+
1551
+ layr = core.getLayer(iden=wlyrs_byview['default'])
1552
+ await self.agenlen(2, layr.getStorNodesByForm('test:str'))
1553
+ await self.agenlen(1, layr.getStorNodesByForm('syn:tag'))
1554
+
1555
+ layr = core.getLayer(iden=wlyrs_byview['prop'])
1556
+ await self.agenlen(1, layr.getStorNodesByForm('test:str'))
1557
+
1558
+ layr = core.getLayer(iden=wlyrs_byview['tags'])
1559
+ await self.agenlen(1, layr.getStorNodesByForm('test:str'))
1560
+ await self.agenlen(1, layr.getStorNodesByForm('syn:tag'))
1561
+
1562
+ layr = core.getLayer(iden=wlyrs_byview['tagp'])
1563
+ await self.agenlen(1, layr.getStorNodesByForm('test:str'))
1564
+ await self.agenlen(0, layr.getStorNodesByForm('syn:tag'))
1565
+
1566
+ layr = core.getLayer(iden=wlyrs_byview['n1eg'])
1567
+ await self.agenlen(1, layr.getStorNodesByForm('test:str'))
1568
+ await self.agenlen(1, layr.getStorNodesByForm('test:int'))
1569
+
1570
+ layr = core.getLayer(iden=wlyrs_byview['n2eg'])
1571
+ await self.agenlen(0, layr.getStorNodesByForm('test:str'))
1572
+ await self.agenlen(1, layr.getStorNodesByForm('test:int'))
1573
+
1574
+ layr = core.getLayer(iden=wlyrs_byview['data'])
1575
+ await self.agenlen(1, layr.getStorNodesByForm('test:str'))
1576
+
1577
+ layr = core.getLayer(iden=wlyrs_byview['noop'])
1578
+ await self.agenlen(0, layr.getStorNodes())
1579
+ await self.agenlen(0, layr.getStorNodesByForm('test:str'))
1580
+
1581
+ finally:
1582
+ s_layer.MIGR_COMMIT_SIZE = oldv
1583
+
1533
1584
  async def test_layer_logedits_default(self):
1534
1585
  async with self.getTestCore() as core:
1535
1586
  self.true(core.getLayer().logedits)
@@ -649,6 +649,42 @@ class LmdbSlabTest(s_t_utils.SynTest):
649
649
  self.eq((b'1', b'1'), next(it))
650
650
  self.raises(StopIteration, next, it)
651
651
 
652
+ async def test_lmdbslab_scanback(self):
653
+
654
+ with self.getTestDir() as dirn:
655
+
656
+ path = os.path.join(dirn, 'test.lmdb')
657
+
658
+ async with await s_lmdbslab.Slab.anit(path, map_size=100000, growsize=10000) as slab:
659
+
660
+ foodup = slab.initdb('foodup', dupsort=True)
661
+ foonodup = slab.initdb('foonodup', dupsort=False)
662
+
663
+ for db in (foodup, foonodup):
664
+ slab.put(b'\x01', b'foo', db=db)
665
+ slab.put(b'\x01\x01', b'bar', db=db)
666
+ slab.put(b'\x01\x03', b'baz', db=db)
667
+ slab.put(b'\x02', b'faz', db=db)
668
+
669
+ items = list(slab.scanByPrefBack(b'\x01', db=foonodup))
670
+ self.eq(items, (
671
+ (b'\x01\x03', b'baz'),
672
+ (b'\x01\x01', b'bar'),
673
+ (b'\x01', b'foo')
674
+ ))
675
+
676
+ self.eq((), list(slab.scanByPrefBack(b'\x00', db=foonodup)))
677
+
678
+ slab.put(b'\x01\x03', b'waz', db=foodup)
679
+
680
+ items = list(slab.scanByPrefBack(b'\x01', db=foodup))
681
+ self.eq(items, (
682
+ (b'\x01\x03', b'waz'),
683
+ (b'\x01\x03', b'baz'),
684
+ (b'\x01\x01', b'bar'),
685
+ (b'\x01', b'foo')
686
+ ))
687
+
652
688
  async def test_lmdbslab_count_empty(self):
653
689
 
654
690
  with self.getTestDir() as dirn:
@@ -508,6 +508,77 @@ bad_uncs = [
508
508
 
509
509
  unc_paths = '\n'.join(good_uncs + bad_uncs)
510
510
 
511
+ cpedata = r'''GOOD DATA
512
+ cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:other
513
+ cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:tspace non matched word
514
+ cpe:2.3:a:*:*:*:*:*:*:*:*:*:*
515
+ cpe:2.3:h:*:*:*:*:*:*:*:*:*:*
516
+ cpe:2.3:o:*:*:*:*:*:*:*:*:*:*
517
+ cpe:2.3:-:*:*:*:*:*:*:*:*:*:*
518
+ cpe:2.3:*:*:*:*:*:*:*:*:*:*:*
519
+ cpe:2.3:*:-:na:*:*:*:*:*:*:*:*
520
+ cpe:2.3:*:.:dot:*:*:*:*:*:*:*:*
521
+ cpe:2.3:*:_:underscore:*:*:*:*:*:*:*:*
522
+
523
+ A few quoted characters
524
+ cpe:2.3:*:\!:quoted:*:*:*:*:*:*:*:*
525
+ cpe:2.3:*:\?:quoted:*:*:*:*:*:*:*:*
526
+ cpe:2.3:*:\*:quoted:*:*:*:*:*:*:*:*
527
+ cpe:2.3:*:\\:escapeescape:*:*:*:*:*:*:*:*
528
+ cpe:2.3:*:langtest:*:*:*:*:-:*:*:*:*
529
+ cpe:2.3:*:langtest:*:*:*:*:*:*:*:*:*
530
+ cpe:2.3:*:langtest:*:*:*:*:en:*:*:*:*
531
+ cpe:2.3:*:langtest:*:*:*:*:usa:*:*:*:*
532
+ cpe:2.3:*:langtest:*:*:*:*:usa-en:*:*:*:*
533
+ cpe:2.3:*:langtest:*:*:*:*:usa-123:*:*:*:*
534
+
535
+ A few examples
536
+ cpe:2.3:a:ntp:ntp:4.2.8:p3:*:*:*:*:*:*
537
+ cpe:2.3:o:microsoft:windows_7:-:sp2:*:*:*:*:*:*
538
+ cpe:2.3:a:hp:insight:7.4.0.1570:-:*:*:online:win2003:x64:*
539
+ cpe:2.3:a:foo\\bar:big\$money_2010:*:*:*:*:special:ipod_touch:80gb:*
540
+ cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*
541
+ cpe:2.3:a:apple:swiftnio_http\/2:1.19.1:*:*:*:*:swift:*:*
542
+
543
+ Some quoted examples
544
+ cpe:2.3:a:fooo:bar_baz\:_beep_bpp_sys:1.1:*:*:*:*:ios:*:*
545
+ cpe:2.3:a:lemonldap-ng:apache\:\:session\:\:browsable:0.9:*:*:*:*:perl:*:*
546
+ cpe:2.3:a:daemon-ng:hurray\:\::0.x:*:*:*:*:*:*:*
547
+ cpe:2.3:a:microsoft:intern\^et_explorer:8.0.6001:beta:*:*:*:*:*:*
548
+
549
+ TEXT examples
550
+ Example double quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:dquotes".
551
+ Example double quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:MiXeDcAsE".
552
+ Example single quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:squotes".
553
+ A CPE at the end of a sentence like this captures the period... cpe:2.3:a:*:*:*:*:*:*:*:*:*:hasperiod.
554
+ Some CPE are exciting! Like this cpe:2.3:a:*:*:*:*:*:*:*:*:*:noexclaim!
555
+ Some CPE are boring! Like this cpe:2.3:a:*:*:*:*:*:*:*:*:*:noslash\
556
+ Unicode endings are omitted cpe:2.3:a:*:*:*:*:*:*:*:*:*:unicodeend0ॐ
557
+ Unicode quotes “cpe:2.3:a:*:*:*:*:*:*:*:*:*:smartquotes”
558
+ cpe:2.3:*:?why??:*:*:*:*:*:*:*:*:*
559
+ cpe:2.3:*:*why*:*:*:*:*:*:*:*:*:*
560
+
561
+ EMBEDDED TEXT
562
+ wordscpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:otherxxx:newp
563
+ wordscpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:otherzzz:
564
+
565
+ BAD values
566
+ cpe:2.3:*:?:spec1:*:*:*:*:*:*:*:*
567
+ cpe:2.3:a:vertex:synapse:*:*:*:NEWP:*:*:*:*
568
+ cpe:2.3:a::::::::::
569
+ cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:ॐ:other
570
+ cpe:2.3:a:vendor:product:version:update:edition
571
+ cpe:2.3:a:opps:bad_quote\\/2:1.19.1:*:*:*:*:swift:*:*
572
+
573
+ # Bad languages
574
+ cpe:2.3:*:langtest:*:*:*:*:a:*:*:*:*
575
+ cpe:2.3:*:langtest:*:*:*:*:aaaa:*:*:*:*
576
+ cpe:2.3:*:langtest:*:*:*:*:usa-o:*:*:*:*
577
+ cpe:2.3:*:langtest:*:*:*:*:usa-omn:*:*:*:*
578
+ cpe:2.3:*:langtest:*:*:*:*:usa-12:*:*:*:*
579
+ cpe:2.3:*:langtest:*:*:*:*:usa-1234:*:*:*:*
580
+ '''
581
+
511
582
  class ScrapeTest(s_t_utils.SynTest):
512
583
 
513
584
  def test_scrape_basic(self):
@@ -1050,77 +1121,7 @@ class ScrapeTest(s_t_utils.SynTest):
1050
1121
  self.eq(erv, fv)
1051
1122
 
1052
1123
  def test_scrape_cpe(self):
1053
- cpedata = r'''
1054
- GOOD DATA
1055
- cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:other
1056
- cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:tspace non matched word
1057
- cpe:2.3:a:*:*:*:*:*:*:*:*:*:*
1058
- cpe:2.3:h:*:*:*:*:*:*:*:*:*:*
1059
- cpe:2.3:o:*:*:*:*:*:*:*:*:*:*
1060
- cpe:2.3:-:*:*:*:*:*:*:*:*:*:*
1061
- cpe:2.3:*:*:*:*:*:*:*:*:*:*:*
1062
- cpe:2.3:*:-:na:*:*:*:*:*:*:*:*
1063
- cpe:2.3:*:.:dot:*:*:*:*:*:*:*:*
1064
- cpe:2.3:*:_:underscore:*:*:*:*:*:*:*:*
1065
-
1066
- A few quoted characters
1067
- cpe:2.3:*:\!:quoted:*:*:*:*:*:*:*:*
1068
- cpe:2.3:*:\?:quoted:*:*:*:*:*:*:*:*
1069
- cpe:2.3:*:\*:quoted:*:*:*:*:*:*:*:*
1070
- cpe:2.3:*:\\:escapeescape:*:*:*:*:*:*:*:*
1071
- cpe:2.3:*:langtest:*:*:*:*:-:*:*:*:*
1072
- cpe:2.3:*:langtest:*:*:*:*:*:*:*:*:*
1073
- cpe:2.3:*:langtest:*:*:*:*:en:*:*:*:*
1074
- cpe:2.3:*:langtest:*:*:*:*:usa:*:*:*:*
1075
- cpe:2.3:*:langtest:*:*:*:*:usa-en:*:*:*:*
1076
- cpe:2.3:*:langtest:*:*:*:*:usa-123:*:*:*:*
1077
-
1078
- A few examples
1079
- cpe:2.3:a:ntp:ntp:4.2.8:p3:*:*:*:*:*:*
1080
- cpe:2.3:o:microsoft:windows_7:-:sp2:*:*:*:*:*:*
1081
- cpe:2.3:a:hp:insight:7.4.0.1570:-:*:*:online:win2003:x64:*
1082
- cpe:2.3:a:foo\\bar:big\$money_2010:*:*:*:*:special:ipod_touch:80gb:*
1083
- cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*
1084
- cpe:2.3:a:apple:swiftnio_http\/2:1.19.1:*:*:*:*:swift:*:*
1085
-
1086
- Some quoted examples
1087
- cpe:2.3:a:fooo:bar_baz\:_beep_bpp_sys:1.1:*:*:*:*:ios:*:*
1088
- cpe:2.3:a:lemonldap-ng:apache\:\:session\:\:browsable:0.9:*:*:*:*:perl:*:*
1089
- cpe:2.3:a:daemon-ng:hurray\:\::0.x:*:*:*:*:*:*:*
1090
- cpe:2.3:a:microsoft:intern\^et_explorer:8.0.6001:beta:*:*:*:*:*:*
1091
-
1092
- TEXT examples
1093
- Example double quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:dquotes".
1094
- Example double quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:MiXeDcAsE".
1095
- Example single quoted cpe value "cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:squotes".
1096
- A CPE at the end of a sentence like this captures the period... cpe:2.3:a:*:*:*:*:*:*:*:*:*:hasperiod.
1097
- Some CPE are exciting! Like this cpe:2.3:a:*:*:*:*:*:*:*:*:*:noexclaim!
1098
- Some CPE are boring! Like this cpe:2.3:a:*:*:*:*:*:*:*:*:*:noslash\
1099
- Unicode endings are omitted cpe:2.3:a:*:*:*:*:*:*:*:*:*:unicodeend0ॐ
1100
- Unicode quotes “cpe:2.3:a:*:*:*:*:*:*:*:*:*:smartquotes”
1101
- cpe:2.3:*:?why??:*:*:*:*:*:*:*:*:*
1102
- cpe:2.3:*:*why*:*:*:*:*:*:*:*:*:*
1103
-
1104
- EMBEDDED TEXT
1105
- wordscpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:otherxxx:newp
1106
- wordscpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:target_hw:otherzzz:
1107
-
1108
- BAD values
1109
- cpe:2.3:*:?:spec1:*:*:*:*:*:*:*:*
1110
- cpe:2.3:a:vertex:synapse:*:*:*:NEWP:*:*:*:*
1111
- cpe:2.3:a::::::::::
1112
- cpe:2.3:a:vendor:product:version:update:edition:lng:sw_edition:target_sw:ॐ:other
1113
- cpe:2.3:a:vendor:product:version:update:edition
1114
- cpe:2.3:a:opps:bad_quote\\/2:1.19.1:*:*:*:*:swift:*:*
1115
-
1116
- # Bad languages
1117
- cpe:2.3:*:langtest:*:*:*:*:a:*:*:*:*
1118
- cpe:2.3:*:langtest:*:*:*:*:aaaa:*:*:*:*
1119
- cpe:2.3:*:langtest:*:*:*:*:usa-o:*:*:*:*
1120
- cpe:2.3:*:langtest:*:*:*:*:usa-omn:*:*:*:*
1121
- cpe:2.3:*:langtest:*:*:*:*:usa-12:*:*:*:*
1122
- cpe:2.3:*:langtest:*:*:*:*:usa-1234:*:*:*:*
1123
- '''
1124
+
1124
1125
  nodes = sorted(set(s_scrape.scrape(cpedata, ptype='it:sec:cpe')))
1125
1126
  nodes.remove(('it:sec:cpe', 'cpe:2.3:*:*:*:*:*:*:*:*:*:*:*'))
1126
1127
  nodes.remove(('it:sec:cpe', 'cpe:2.3:*:-:na:*:*:*:*:*:*:*:*'))
@@ -584,7 +584,10 @@ class SnapTest(s_t_utils.SynTest):
584
584
  async def test_snap_editor(self):
585
585
 
586
586
  async with self.getTestCore() as core:
587
- nodes = await core.nodes('[ media:news=63381924986159aff183f0c85bd8ebad +(refs)> {[ inet:fqdn=vertex.link ]} ]')
587
+
588
+ await core.nodes('$lib.model.ext.addTagProp(test, (str, ({})), ({}))')
589
+ await core.nodes('[ media:news=63381924986159aff183f0c85bd8ebad +(refs)> {[ inet:fqdn=vertex.link ]} +#foo ]')
590
+
588
591
  root = core.auth.rootuser
589
592
  async with await core.view.snap(user=root) as snap:
590
593
  async with snap.getEditor() as editor:
@@ -608,6 +611,14 @@ class SnapTest(s_t_utils.SynTest):
608
611
  self.len(1, nodeedits)
609
612
  self.len(1, nodeedits[0][2])
610
613
 
614
+ self.false(await news.hasData('foo'))
615
+ await news.setData('foo', 'bar')
616
+ self.true(await news.hasData('foo'))
617
+
618
+ self.false(news.hasTagProp('foo', 'test'))
619
+ await news.setTagProp('foo', 'test', 'bar')
620
+ self.true(news.hasTagProp('foo', 'test'))
621
+
611
622
  async with snap.getEditor() as editor:
612
623
  news = await editor.addNode('media:news', '63381924986159aff183f0c85bd8ebad')
613
624
 
@@ -629,6 +640,10 @@ class SnapTest(s_t_utils.SynTest):
629
640
  self.false(await news.delEdge('pwns', 1))
630
641
  self.false(await news.delEdge('pwns', 'bar'))
631
642
 
643
+ self.true(await news.hasData('foo'))
644
+
645
+ self.true(news.hasTagProp('foo', 'test'))
646
+
632
647
  self.len(1, await core.nodes('media:news -(pwns)> *'))
633
648
 
634
649
  self.len(1, await core.nodes('[ test:ro=foo :writeable=hehe :readable=haha ]'))