synapse 2.164.0__py311-none-any.whl → 2.166.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 (89) hide show
  1. synapse/axon.py +3 -3
  2. synapse/cmds/cortex.py +1 -6
  3. synapse/common.py +7 -1
  4. synapse/cortex.py +145 -192
  5. synapse/datamodel.py +36 -1
  6. synapse/lib/agenda.py +87 -97
  7. synapse/lib/aha.py +51 -0
  8. synapse/lib/ast.py +22 -23
  9. synapse/lib/base.py +0 -6
  10. synapse/lib/boss.py +3 -0
  11. synapse/lib/cell.py +70 -39
  12. synapse/lib/certdir.py +9 -0
  13. synapse/lib/hiveauth.py +65 -12
  14. synapse/lib/httpapi.py +1 -0
  15. synapse/lib/modelrev.py +121 -33
  16. synapse/lib/modules.py +1 -0
  17. synapse/lib/nexus.py +64 -26
  18. synapse/lib/parser.py +2 -0
  19. synapse/lib/schemas.py +14 -0
  20. synapse/lib/snap.py +50 -4
  21. synapse/lib/storm.lark +4 -3
  22. synapse/lib/storm.py +96 -22
  23. synapse/lib/storm_format.py +1 -0
  24. synapse/lib/stormlib/aha.py +7 -1
  25. synapse/lib/stormlib/auth.py +13 -5
  26. synapse/lib/stormlib/cache.py +202 -0
  27. synapse/lib/stormlib/cortex.py +147 -8
  28. synapse/lib/stormlib/gen.py +53 -6
  29. synapse/lib/stormlib/math.py +1 -1
  30. synapse/lib/stormlib/model.py +11 -1
  31. synapse/lib/stormlib/spooled.py +109 -0
  32. synapse/lib/stormlib/vault.py +1 -1
  33. synapse/lib/stormtypes.py +113 -17
  34. synapse/lib/trigger.py +36 -47
  35. synapse/lib/types.py +29 -2
  36. synapse/lib/version.py +2 -2
  37. synapse/lib/view.py +80 -53
  38. synapse/models/economic.py +174 -5
  39. synapse/models/files.py +2 -0
  40. synapse/models/inet.py +77 -2
  41. synapse/models/infotech.py +12 -12
  42. synapse/models/orgs.py +72 -21
  43. synapse/models/person.py +40 -11
  44. synapse/models/risk.py +78 -24
  45. synapse/models/science.py +102 -0
  46. synapse/telepath.py +117 -35
  47. synapse/tests/test_cortex.py +84 -158
  48. synapse/tests/test_datamodel.py +22 -0
  49. synapse/tests/test_lib_agenda.py +52 -96
  50. synapse/tests/test_lib_aha.py +126 -4
  51. synapse/tests/test_lib_ast.py +412 -6
  52. synapse/tests/test_lib_cell.py +24 -8
  53. synapse/tests/test_lib_certdir.py +32 -0
  54. synapse/tests/test_lib_grammar.py +9 -1
  55. synapse/tests/test_lib_httpapi.py +0 -1
  56. synapse/tests/test_lib_jupyter.py +0 -1
  57. synapse/tests/test_lib_modelrev.py +41 -0
  58. synapse/tests/test_lib_nexus.py +38 -0
  59. synapse/tests/test_lib_storm.py +95 -5
  60. synapse/tests/test_lib_stormlib_cache.py +272 -0
  61. synapse/tests/test_lib_stormlib_cortex.py +71 -0
  62. synapse/tests/test_lib_stormlib_gen.py +37 -2
  63. synapse/tests/test_lib_stormlib_model.py +2 -0
  64. synapse/tests/test_lib_stormlib_spooled.py +190 -0
  65. synapse/tests/test_lib_stormlib_vault.py +12 -3
  66. synapse/tests/test_lib_stormsvc.py +0 -10
  67. synapse/tests/test_lib_stormtypes.py +60 -8
  68. synapse/tests/test_lib_trigger.py +20 -2
  69. synapse/tests/test_lib_types.py +17 -1
  70. synapse/tests/test_model_economic.py +114 -0
  71. synapse/tests/test_model_files.py +2 -0
  72. synapse/tests/test_model_inet.py +73 -1
  73. synapse/tests/test_model_infotech.py +2 -2
  74. synapse/tests/test_model_orgs.py +10 -1
  75. synapse/tests/test_model_risk.py +30 -2
  76. synapse/tests/test_model_science.py +59 -0
  77. synapse/tests/test_model_syn.py +0 -1
  78. synapse/tests/test_telepath.py +30 -7
  79. synapse/tests/test_tools_modrole.py +81 -0
  80. synapse/tests/test_tools_moduser.py +105 -0
  81. synapse/tools/modrole.py +59 -7
  82. synapse/tools/moduser.py +78 -10
  83. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/METADATA +2 -2
  84. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/RECORD +87 -83
  85. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/WHEEL +1 -1
  86. synapse/lib/provenance.py +0 -111
  87. synapse/tests/test_lib_provenance.py +0 -37
  88. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/LICENSE +0 -0
  89. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/top_level.txt +0 -0
@@ -20,7 +20,6 @@ import synapse.lib.storm as s_storm
20
20
  import synapse.lib.hashset as s_hashset
21
21
  import synapse.lib.httpapi as s_httpapi
22
22
  import synapse.lib.modelrev as s_modelrev
23
- import synapse.lib.provenance as s_provenance
24
23
  import synapse.lib.stormtypes as s_stormtypes
25
24
 
26
25
  import synapse.tests.utils as s_test
@@ -490,6 +489,12 @@ class StormTypesTest(s_test.SynTest):
490
489
  nodes = await core.nodes('test:str=foo $node.difftags((["foo", "baz", ""]), apply=$lib.true)')
491
490
  self.sorteq(nodes[0].tags, ['foo', 'baz'])
492
491
 
492
+ nodes = await core.nodes('test:str=foo $node.difftags((["foo-bar", ""]), apply=$lib.true, norm=$lib.true)')
493
+ self.sorteq(nodes[0].tags, ['foo_bar'])
494
+
495
+ nodes = await core.nodes('test:str=foo $node.difftags((["foo-bar", "foo", "baz"]), apply=$lib.true)')
496
+ self.sorteq(nodes[0].tags, ['foo', 'baz'])
497
+
493
498
  await core.setTagModel("foo", "regex", (None, "[a-zA-Z]{3}"))
494
499
  async with await core.snap() as snap:
495
500
  snap.strict = False
@@ -501,7 +506,6 @@ class StormTypesTest(s_test.SynTest):
501
506
  'name': 'foo',
502
507
  'desc': 'test',
503
508
  'version': (0, 0, 1),
504
- 'synapse_minversion': [2, 144, 0],
505
509
  'synapse_version': '>=2.8.0,<3.0.0',
506
510
  'modules': [
507
511
  {
@@ -1323,6 +1327,9 @@ class StormTypesTest(s_test.SynTest):
1323
1327
 
1324
1328
  self.eq('foo bar baz faz', await core.callStorm('return($lib.regex.replace("[ ]{2,}", " ", "foo bar baz faz"))'))
1325
1329
 
1330
+ self.eq(r'foo\.bar\.baz', await core.callStorm('return($lib.regex.escape("foo.bar.baz"))'))
1331
+ self.eq(r'foo\ bar\ baz', await core.callStorm('return($lib.regex.escape("foo bar baz"))'))
1332
+
1326
1333
  self.eq(((1, 2, 3)), await core.callStorm('return(("[1, 2, 3]").json())'))
1327
1334
 
1328
1335
  with self.raises(s_exc.BadJsonText):
@@ -4504,7 +4511,6 @@ class StormTypesTest(s_test.SynTest):
4504
4511
 
4505
4512
  MONO_DELT = 1543827303.0
4506
4513
  unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp()
4507
- s_provenance.reset()
4508
4514
 
4509
4515
  def timetime():
4510
4516
  return unixtime
@@ -4838,6 +4844,7 @@ class StormTypesTest(s_test.SynTest):
4838
4844
 
4839
4845
  self.stormIsInPrint('last result: finished successfully with 0 nodes', mesgs)
4840
4846
  self.stormIsInPrint('entries: <None>', mesgs)
4847
+ self.stormIsInPrint('pool: false', mesgs)
4841
4848
 
4842
4849
  # Test 'stat' command
4843
4850
  mesgs = await core.stormlist('cron.stat xxx')
@@ -4911,7 +4918,7 @@ class StormTypesTest(s_test.SynTest):
4911
4918
  self.stormIsInErr('data.iden must match pattern', msgs)
4912
4919
 
4913
4920
  opts = {'vars': {'iden': 'cd263bd133a5dafa1e1c5e9a01d9d486'}}
4914
- q = "cron.add --iden $iden --day +1 --minute 14 {[test:guid=$lib.guid()]}"
4921
+ q = "cron.add --pool --iden $iden --day +1 --minute 14 {[test:guid=$lib.guid()]}"
4915
4922
  msgs = await core.stormlist(q, opts=opts)
4916
4923
  self.stormIsInPrint('Created cron job: cd263bd133a5dafa1e1c5e9a01d9d486', msgs)
4917
4924
 
@@ -6432,6 +6439,9 @@ words\tword\twrd'''
6432
6439
 
6433
6440
  fork00 = await core.callStorm('return($lib.view.get().fork().iden)')
6434
6441
 
6442
+ with self.raises(s_exc.BadState):
6443
+ await core.callStorm('$lib.view.get().setMergeComment("that doesnt exist")', opts={'user': root.iden, 'view': fork00})
6444
+
6435
6445
  msgs = await core.stormlist('[ inet:fqdn=vertex.link ]', opts={'view': fork00})
6436
6446
  self.stormHasNoWarnErr(msgs)
6437
6447
 
@@ -6452,14 +6462,23 @@ words\tword\twrd'''
6452
6462
  self.nn(merge['created'])
6453
6463
  self.eq(merge['comment'], 'woot')
6454
6464
  self.eq(merge['creator'], core.auth.rootuser.iden)
6465
+ self.none(merge.get('updated'))
6466
+
6467
+ merging = 'return($lib.view.get().getMergingViews()) '
6468
+
6469
+ self.eq([fork00], await core.callStorm(merging))
6455
6470
 
6456
6471
  with self.raises(s_exc.AuthDeny):
6457
6472
  core.getView(fork00).reqValidVoter(root.iden)
6458
6473
 
6474
+ await core.callStorm('$lib.view.get().setMergeComment("mergin some dataaz")', opts={'view': fork00})
6475
+
6459
6476
  merge = await core.callStorm('return($lib.view.get().getMergeRequest())', opts={'view': fork00})
6460
6477
  self.nn(merge['iden'])
6461
6478
  self.nn(merge['created'])
6462
- self.eq(merge['comment'], 'woot')
6479
+ self.nn(merge['updated'])
6480
+ self.gt(merge['updated'], merge['created'])
6481
+ self.eq(merge['comment'], 'mergin some dataaz')
6463
6482
  self.eq(merge['creator'], core.auth.rootuser.iden)
6464
6483
 
6465
6484
  with self.raises(s_exc.AuthDeny):
@@ -6479,6 +6498,21 @@ words\tword\twrd'''
6479
6498
  self.eq(vote['user'], visi.iden)
6480
6499
  self.eq(vote['comment'], 'fixyourstuff')
6481
6500
 
6501
+ forkview = core.getView(fork00)
6502
+
6503
+ with self.raises(s_exc.BadState):
6504
+ await core.callStorm('$lib.view.get().setMergeVoteComment("wait that doesnt exist")', opts={'view': fork00, 'user': newp.iden})
6505
+
6506
+ with self.raises(s_exc.AuthDeny):
6507
+ await core.callStorm('$lib.view.get().setMergeComment("no wait you cant do that")', opts={'view': fork00, 'user': newp.iden})
6508
+
6509
+ await core.callStorm('$lib.view.get().setMergeVoteComment("no really, fix your stuff")', opts=opts)
6510
+ votes = [vote async for vote in forkview.getMergeVotes()]
6511
+ self.len(1, votes)
6512
+ self.eq(votes[0]['comment'], 'no really, fix your stuff')
6513
+ self.nn(votes[0]['updated'])
6514
+ self.gt(votes[0]['updated'], votes[0]['created'])
6515
+
6482
6516
  opts = {'user': newp.iden, 'view': fork00}
6483
6517
  vote = await core.callStorm('return($lib.view.get().setMergeVote())', opts=opts)
6484
6518
  self.nn(vote['offset'])
@@ -6497,8 +6531,6 @@ words\tword\twrd'''
6497
6531
  opts = {'user': newp.iden, 'view': fork00, 'vars': {'visi': visi.iden}}
6498
6532
  await core.callStorm('return($lib.view.get().delMergeVote(useriden=$visi))', opts=opts)
6499
6533
 
6500
- forkview = core.getView(fork00)
6501
-
6502
6534
  # removing the last veto triggers the merge
6503
6535
  opts = {'user': visi.iden, 'view': fork00}
6504
6536
  await core.callStorm('return($lib.view.get().delMergeVote())', opts=opts)
@@ -6511,8 +6543,13 @@ words\tword\twrd'''
6511
6543
  self.none(core.getView(fork00))
6512
6544
  nodes = await core.nodes('inet:fqdn')
6513
6545
  self.len(2, nodes)
6546
+
6547
+ # previously successful merges
6514
6548
  self.len(1, await core.callStorm('$list = ([]) for $merge in $lib.view.get().getMerges() { $list.append($merge) } fini { return($list) }'))
6515
6549
 
6550
+ # current open merge requests
6551
+ self.eq([], await core.callStorm(merging))
6552
+
6516
6553
  # test out the delMergeRequest logic / cleanup
6517
6554
  forkdef = await core.getView().fork()
6518
6555
 
@@ -6520,6 +6557,7 @@ words\tword\twrd'''
6520
6557
 
6521
6558
  opts = {'view': fork.iden}
6522
6559
  self.nn(await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts))
6560
+ self.eq([fork.iden], await core.callStorm(merging))
6523
6561
 
6524
6562
  # confirm that you may not re-parent to a view with a merge request
6525
6563
  layr = await core.addLayer()
@@ -6535,6 +6573,8 @@ words\tword\twrd'''
6535
6573
  self.nn(await core.callStorm('return($lib.view.get().delMergeRequest())', opts=opts))
6536
6574
  self.len(0, [vote async for vote in fork.getMergeVotes()])
6537
6575
 
6576
+ self.eq([], await core.callStorm(merging))
6577
+
6538
6578
  # test coverage for beholder progress events...
6539
6579
  forkdef = await core.getView().fork()
6540
6580
  fork = core.getView(forkdef.get('iden'))
@@ -6543,10 +6583,12 @@ words\tword\twrd'''
6543
6583
  await core.stormlist('[ inet:ipv4=1.2.3.0/20 ]', opts=opts)
6544
6584
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6545
6585
 
6586
+ self.eq([fork.iden], await core.callStorm(merging))
6587
+
6546
6588
  nevents = 8
6547
6589
  if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
6548
6590
  # view:merge:vote:set fires twice
6549
- nevents = nevents + 1
6591
+ nevents += 1
6550
6592
  waiter = core.waiter(nevents, 'cell:beholder')
6551
6593
 
6552
6594
  opts = {'view': fork.iden, 'user': visi.iden}
@@ -6569,6 +6611,8 @@ words\tword\twrd'''
6569
6611
  self.eq(msgs[-1][1]['info']['merge']['creator'], core.auth.rootuser.iden)
6570
6612
  self.eq(msgs[-1][1]['info']['votes'][0]['user'], visi.iden)
6571
6613
 
6614
+ self.eq([], await core.callStorm(merging))
6615
+
6572
6616
  # test coverage for bad state for merge request
6573
6617
  fork00 = await core.getView().fork()
6574
6618
  fork01 = await core.getView(fork00['iden']).fork()
@@ -6577,8 +6621,11 @@ words\tword\twrd'''
6577
6621
  with self.raises(s_exc.BadState):
6578
6622
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6579
6623
 
6624
+ self.eq([], await core.callStorm(merging))
6625
+
6580
6626
  await core.delView(fork01['iden'])
6581
6627
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6628
+ self.eq([fork00['iden']], await core.callStorm(merging))
6582
6629
 
6583
6630
  core.getView().layers[0].readonly = True
6584
6631
  with self.raises(s_exc.BadState):
@@ -6597,6 +6644,8 @@ words\tword\twrd'''
6597
6644
  await core.stormlist('[ inet:ipv4=5.5.5.5 ]', opts=opts)
6598
6645
  await core.callStorm('return($lib.view.get().setMergeRequest())', opts=opts)
6599
6646
 
6647
+ self.eq(set([fork.iden, fork00['iden']]), set(await core.callStorm(merging)))
6648
+
6600
6649
  # hamstring the runViewMerge method on the new view
6601
6650
  async def fake():
6602
6651
  return
@@ -6631,3 +6680,6 @@ words\tword\twrd'''
6631
6680
 
6632
6681
  msgs = await core.stormlist('$lib.view.get().set(quorum, $lib.null)')
6633
6682
  self.stormHasNoWarnErr(msgs)
6683
+
6684
+ with self.raises(s_exc.BadState):
6685
+ await core.callStorm(merging)
@@ -354,6 +354,24 @@ class TrigTest(s_t_utils.SynTest):
354
354
  await self.asyncraises(s_exc.NoSuchIden, view.delTrigger('newp'))
355
355
  await self.asyncraises(s_exc.NoSuchIden, view.setTriggerInfo('newp', 'enabled', True))
356
356
 
357
+ # mop up some coverage
358
+ msgs = await core.stormlist('trigger.add tag:del --form inet:ipv4 --tag zoinks --query { [+#bar] }')
359
+ self.stormHasNoWarnErr(msgs)
360
+
361
+ msgs = await core.stormlist('trigger.add tag:del --tag zoinks.* --query { [+#faz] }')
362
+ self.stormHasNoWarnErr(msgs)
363
+
364
+ nodes = await core.nodes('[ inet:ipv4=1.2.3.4 +#zoinks.foo -#zoinks ]')
365
+
366
+ self.len(1, nodes)
367
+ self.nn(nodes[0].getTag('bar'))
368
+ self.nn(nodes[0].getTag('faz'))
369
+
370
+ # coverage for migration mode
371
+ await core.nodes('[inet:fqdn=vertex.link +#foo]') # for additional migration mode trigger tests below
372
+ with core.enterMigrationMode():
373
+ await core.nodes('inet:fqdn=vertex.link [ +#bar -#foo ]')
374
+
357
375
  async def test_trigger_delete(self):
358
376
 
359
377
  async with self.getTestCore() as core:
@@ -756,7 +774,7 @@ class TrigTest(s_t_utils.SynTest):
756
774
  await core.nodes('trigger.add edge:add --verb r* --n2form test:int --query { [ +#n2 ] }')
757
775
  await core.nodes('trigger.add edge:add --verb no** --form test:int --n2form test:str --query { [ +#both ] }')
758
776
 
759
- async with core.enterMigrationMode():
777
+ with core.enterMigrationMode():
760
778
  nodes = await core.nodes('[test:int=123 +(foo:beep:boop)> { [test:str=neato] }]')
761
779
  self.len(1, nodes)
762
780
  self.notin('foo', nodes[0].tags)
@@ -816,7 +834,7 @@ class TrigTest(s_t_utils.SynTest):
816
834
  await core.nodes('trigger.add edge:del --verb r* --n2form test:int --query { [ +#del.two ] }')
817
835
  await core.nodes('trigger.add edge:del --verb no** --form test:int --n2form test:str --query { [ +#del.all ] }')
818
836
 
819
- async with core.enterMigrationMode():
837
+ with core.enterMigrationMode():
820
838
  nodes = await core.nodes('test:int=123 | [ -(foo:beep:boop)> { test:str=neato } ]')
821
839
  self.len(1, nodes)
822
840
  self.notin('del.none', nodes[0].tags)
@@ -6,6 +6,7 @@ import synapse.datamodel as s_datamodel
6
6
 
7
7
  import synapse.lib.time as s_time
8
8
  import synapse.lib.const as s_const
9
+ import synapse.lib.stormtypes as s_stormtypes
9
10
 
10
11
  import synapse.tests.utils as s_t_utils
11
12
  from synapse.tests.utils import alist
@@ -39,7 +40,7 @@ class TypesTest(s_t_utils.SynTest):
39
40
  with self.raises(s_exc.BadTypeValu):
40
41
  mass.norm('newps')
41
42
 
42
- def test_velocity(self):
43
+ async def test_velocity(self):
43
44
  model = s_datamodel.Model()
44
45
  velo = model.type('velocity')
45
46
 
@@ -78,6 +79,12 @@ class TypesTest(s_t_utils.SynTest):
78
79
  relv = velo.clone({'relative': True})
79
80
  self.eq(-2777, relv.norm('-10k/h')[0])
80
81
 
82
+ self.eq(1, velo.norm('1.23')[0])
83
+
84
+ async with self.getTestCore() as core:
85
+ nodes = await core.nodes('[transport:sea:telem=(foo,) :speed=(1.1 * 2) ]')
86
+ self.eq(2, nodes[0].get('speed'))
87
+
81
88
  def test_hugenum(self):
82
89
 
83
90
  model = s_datamodel.Model()
@@ -248,6 +255,9 @@ class TypesTest(s_t_utils.SynTest):
248
255
  self.eq(t.norm('0'), (0, {}))
249
256
  self.eq(t.norm('1'), (1, {}))
250
257
 
258
+ self.eq(t.norm(s_stormtypes.Number('1')), (1, {}))
259
+ self.eq(t.norm(s_stormtypes.Number('0')), (0, {}))
260
+
251
261
  for s in ('trUe', 'T', 'y', ' YES', 'On '):
252
262
  self.eq(t.norm(s), (1, {}))
253
263
 
@@ -486,6 +496,7 @@ class TypesTest(s_t_utils.SynTest):
486
496
  self.eq(t.norm(True)[0], 1)
487
497
  self.eq(t.norm(False)[0], 0)
488
498
  self.eq(t.norm(decimal.Decimal('1.0'))[0], 1)
499
+ self.eq(t.norm(s_stormtypes.Number('1.0'))[0], 1)
489
500
 
490
501
  # Test merge
491
502
  self.eq(30, t.merge(20, 30))
@@ -572,6 +583,7 @@ class TypesTest(s_t_utils.SynTest):
572
583
  self.true(math.isnan(t.norm('NaN')[0]))
573
584
  self.eq(t.norm('-0.0')[0], -0.0)
574
585
  self.eq(t.norm('42')[0], 42.0)
586
+ self.eq(t.norm(s_stormtypes.Number('1.23'))[0], 1.23)
575
587
  minmax = model.type('float').clone({'min': -10.0, 'max': 100.0, 'maxisvalid': True, 'minisvalid': False})
576
588
  self.raises(s_exc.BadTypeValu, minmax.norm, 'NaN')
577
589
  self.raises(s_exc.BadTypeValu, minmax.norm, '-inf')
@@ -610,6 +622,8 @@ class TypesTest(s_t_utils.SynTest):
610
622
  self.len(1, await core.nodes('[ test:float=42.0 :open=0.001]'))
611
623
  self.len(1, await core.nodes('[ test:float=42.0 :open=359.0]'))
612
624
 
625
+ self.eq(5, await core.callStorm('return($lib.cast(int, (5.5)))'))
626
+
613
627
  async def test_ival(self):
614
628
  model = s_datamodel.Model()
615
629
  ival = model.types.get('ival')
@@ -621,6 +635,7 @@ class TypesTest(s_t_utils.SynTest):
621
635
  self.eq((0, 5356800000), ival.norm((0, '1970-03-04'))[0])
622
636
  self.eq((1451606400000, 1451606400001), ival.norm('2016')[0])
623
637
  self.eq((1451606400000, 1451606400001), ival.norm(1451606400000)[0])
638
+ self.eq((1451606400000, 1451606400001), ival.norm(s_stormtypes.Number(1451606400000))[0])
624
639
  self.eq((1451606400000, 1451606400001), ival.norm('2016')[0])
625
640
  self.eq((1451606400000, 1483228800000), ival.norm(('2016', ' 2017'))[0])
626
641
  self.eq((1451606400000, 1483228800000), ival.norm(('2016-01-01', ' 2017'))[0])
@@ -1077,6 +1092,7 @@ class TypesTest(s_t_utils.SynTest):
1077
1092
  self.eq('1234567891.1234567', flt.norm(1234567890.123456790123456790123456789 + 1)[0])
1078
1093
  self.eq('1234567890.1234567', flt.norm(1234567890.123456790123456790123456789 + 0.0000000001)[0])
1079
1094
  self.eq('2.718281828459045', flt.norm(2.718281828459045)[0])
1095
+ self.eq('1.23', flt.norm(s_stormtypes.Number(1.23))[0])
1080
1096
 
1081
1097
  def test_syntag(self):
1082
1098
 
@@ -111,9 +111,11 @@ class EconTest(s_utils.SynTest):
111
111
  text = f'''[
112
112
  econ:acct:payment="*"
113
113
 
114
+ :to:account=*
114
115
  :to:contact={bycont}
115
116
  :to:coinaddr=(btc, 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)
116
117
 
118
+ :from:account=*
117
119
  :from:contact={fromcont}
118
120
  :from:coinaddr=(btc, 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2)
119
121
 
@@ -132,6 +134,9 @@ class EconTest(s_utils.SynTest):
132
134
  self.len(1, await core.nodes('econ:acct:payment -> econ:pay:card'))
133
135
  self.len(2, await core.nodes('econ:acct:payment -> ps:contact | uniq'))
134
136
 
137
+ self.len(1, await core.nodes('econ:acct:payment :to:account -> econ:bank:account'))
138
+ self.len(1, await core.nodes('econ:acct:payment :from:account -> econ:bank:account'))
139
+
135
140
  nodes = await core.nodes('''
136
141
  [ econ:fin:exchange=(us,nasdaq) :name=nasdaq :currency=usd :org=* ]
137
142
  ''')
@@ -239,3 +244,112 @@ class EconTest(s_utils.SynTest):
239
244
  self.eq(nodes[0].get('price'), '100')
240
245
  self.len(1, await core.nodes('econ:receipt:item -> econ:purchase'))
241
246
  self.len(1, await core.nodes('econ:receipt:item -> biz:product +:name=bananna'))
247
+
248
+ nodes = await core.nodes('''
249
+ [ econ:bank:account=*
250
+ :number=1234
251
+ :type=checking
252
+ :aba:rtn=123456789
253
+ :iban=VV09WootWoot
254
+ :issuer={ gen.ou.org "bank of visi" }
255
+ :issuer:name="bank of visi"
256
+ :contact={[ ps:contact=* :name=visi ]}
257
+ :currency=usd
258
+ :balance=*
259
+ ]
260
+ ''')
261
+ self.len(1, nodes)
262
+ self.nn(nodes[0].get('issuer'))
263
+ self.nn(nodes[0].get('balance'))
264
+ self.nn(nodes[0].get('contact'))
265
+ self.eq('1234', nodes[0].get('number'))
266
+ self.eq('usd', nodes[0].get('currency'))
267
+ self.eq('checking.', nodes[0].get('type'))
268
+ self.eq('VV09WootWoot', nodes[0].get('iban'))
269
+ self.eq('bank of visi', nodes[0].get('issuer:name'))
270
+ self.len(1, await core.nodes('econ:bank:account -> ou:org'))
271
+ self.len(1, await core.nodes('econ:bank:account -> ou:name'))
272
+ self.len(1, await core.nodes('econ:bank:account -> econ:bank:aba:rtn'))
273
+ self.len(1, await core.nodes('econ:bank:account -> econ:bank:balance'))
274
+ self.len(1, await core.nodes('econ:bank:account -> ps:contact +:name=visi'))
275
+ self.len(1, await core.nodes('econ:bank:account -> econ:bank:account:type:taxonomy'))
276
+
277
+ nodes = await core.nodes('''[
278
+ econ:bank:swift:bic=DEUTDEFFXXX
279
+ :business={ gen.ou.org "Deutsche Bank" }
280
+ :office=*
281
+ ]''')
282
+ self.len(1, nodes)
283
+ self.len(1, await core.nodes('econ:bank:swift:bic -> ou:org +:name="deutsche bank"'))
284
+ self.len(1, await core.nodes('econ:bank:swift:bic -> ps:contact'))
285
+
286
+ nodes = await core.nodes('''[
287
+ econ:bank:balance=*
288
+ :account={econ:bank:account | limit 1}
289
+ :time=2024-03-19
290
+ :amount=99
291
+ ]''')
292
+ self.len(1, nodes)
293
+ self.nn(nodes[0].get('account'))
294
+ self.eq(1710806400000, nodes[0].get('time'))
295
+ self.eq('99', nodes[0].get('amount'))
296
+
297
+ nodes = await core.nodes('''[
298
+ econ:bank:statement=*
299
+ :account={econ:bank:account | limit 1}
300
+ :period=202403*
301
+ :starting:balance=99
302
+ :ending:balance=999
303
+ ]''')
304
+ self.len(1, nodes)
305
+ self.nn(nodes[0].get('account'))
306
+ self.eq('99', nodes[0].get('starting:balance'))
307
+ self.eq('999', nodes[0].get('ending:balance'))
308
+ self.eq((1709251200000, 1709251200001), nodes[0].get('period'))
309
+
310
+ nodes = await core.nodes('''[
311
+ econ:bank:aba:rtn=123456789
312
+ :bank={ gen.ou.org "deutsche bank" }
313
+ :bank:name="deutsche bank"
314
+ ]''')
315
+ self.len(1, nodes)
316
+ self.len(1, await core.nodes('econ:bank:aba:rtn=123456789 -> ou:name'))
317
+ self.len(1, await core.nodes('econ:bank:aba:rtn=123456789 -> ou:org +:name="deutsche bank"'))
318
+
319
+ nodes = await core.nodes('''[
320
+ econ:acct:receipt=*
321
+ :amount=99
322
+ :currency=usd
323
+ :purchase=*
324
+ :issued=2024-03-19
325
+ :issuer={ ps:contact:name=visi }
326
+ :recipient={ ps:contact:name=visi }
327
+ ]''')
328
+ self.len(1, nodes)
329
+ self.eq('99', nodes[0].get('amount'))
330
+ self.eq('usd', nodes[0].get('currency'))
331
+ self.eq(1710806400000, nodes[0].get('issued'))
332
+ self.len(1, await core.nodes('econ:acct:receipt -> econ:purchase'))
333
+ self.len(1, await core.nodes('econ:acct:receipt :issuer -> ps:contact'))
334
+ self.len(1, await core.nodes('econ:acct:receipt :recipient -> ps:contact'))
335
+
336
+ nodes = await core.nodes('''[
337
+ econ:acct:invoice=*
338
+ :paid=(false)
339
+ :amount=99
340
+ :currency=usd
341
+ :purchase=*
342
+ :due=2024-03-19
343
+ :issued=2024-03-19
344
+ :issuer={ ps:contact:name=visi }
345
+ :recipient={ ps:contact:name=visi }
346
+ ]''')
347
+ self.len(1, nodes)
348
+ self.eq('99', nodes[0].get('amount'))
349
+ self.eq('usd', nodes[0].get('currency'))
350
+ self.eq(0, nodes[0].get('paid'))
351
+ self.eq(1710806400000, nodes[0].get('due'))
352
+ self.eq(1710806400000, nodes[0].get('issued'))
353
+ self.len(1, await core.nodes('econ:acct:invoice -> econ:purchase'))
354
+ self.len(1, await core.nodes('econ:acct:invoice :issuer -> ps:contact'))
355
+ self.len(1, await core.nodes('econ:acct:invoice :recipient -> ps:contact'))
@@ -474,6 +474,7 @@ class FileTest(s_t_utils.SynTest):
474
474
  self.eq(('foo', 'bar'), n.get('file:data'))
475
475
  self.eq('aaaa', n.get('desc'))
476
476
  self.eq('bbbb', n.get('comment'))
477
+ self.eq('foo bar', n.get('text'))
477
478
  self.eq(1578236238000, n.get('created'))
478
479
  self.eq('a6b4', n.get('imageid'))
479
480
  self.eq(conguid, n.get('author'))
@@ -494,6 +495,7 @@ class FileTest(s_t_utils.SynTest):
494
495
  :file:data=(foo, bar)
495
496
  :desc=aaaa
496
497
  :comment=bbbb
498
+ :text=" Foo Bar "
497
499
  :created="2020-01-05 14:57:18"
498
500
  :imageid=a6b4
499
501
  :author=$conguid
@@ -2650,6 +2650,9 @@ class InetModelTest(s_t_utils.SynTest):
2650
2650
  async def test_model_inet_email_message(self):
2651
2651
 
2652
2652
  async with self.getTestCore() as core:
2653
+
2654
+ flow = s_common.guid()
2655
+
2653
2656
  q = '''
2654
2657
  [
2655
2658
  inet:email:message="*"
@@ -2665,18 +2668,20 @@ class InetModelTest(s_t_utils.SynTest):
2665
2668
  :received:from:ipv4=1.2.3.4
2666
2669
  :received:from:ipv6="::1"
2667
2670
  :received:from:fqdn=smtp.vertex.link
2671
+ :flow=$flow
2668
2672
  ]
2669
2673
 
2670
2674
  {[( inet:email:message:link=($node, https://www.vertex.link) :text=Vertex )]}
2671
2675
  {[( inet:email:message:attachment=($node, "*") :name=sploit.exe )]}
2672
2676
  '''
2673
- nodes = await core.nodes(q)
2677
+ nodes = await core.nodes(q, opts={'vars': {'flow': flow}})
2674
2678
  self.len(1, nodes)
2675
2679
 
2676
2680
  self.eq(nodes[0].get('cc'), ('baz@faz.org', 'foo@bar.com'))
2677
2681
  self.eq(nodes[0].get('received:from:ipv6'), '::1')
2678
2682
  self.eq(nodes[0].get('received:from:ipv4'), 0x01020304)
2679
2683
  self.eq(nodes[0].get('received:from:fqdn'), 'smtp.vertex.link')
2684
+ self.eq(nodes[0].get('flow'), flow)
2680
2685
 
2681
2686
  self.len(1, await core.nodes('inet:email:message:to=woot@woot.com'))
2682
2687
  self.len(1, await core.nodes('inet:email:message:date=2015'))
@@ -2762,3 +2767,70 @@ class InetModelTest(s_t_utils.SynTest):
2762
2767
  self.eq(nodes[0].get('client'), 'tcp://1.2.3.4')
2763
2768
  self.eq(nodes[0].get('client:ipv4'), 0x01020304)
2764
2769
  self.eq(nodes[0].get('client:ipv6'), '::1')
2770
+
2771
+ async def test_model_inet_tls_handshake(self):
2772
+
2773
+ async with self.getTestCore() as core:
2774
+ props = {
2775
+ 'ja3': '1' * 32,
2776
+ 'ja3s': '2' * 32,
2777
+ 'client': 'tcp://1.2.3.4:8888',
2778
+ 'server': 'tcp://5.6.7.8:9999'
2779
+ }
2780
+
2781
+ nodes = await core.nodes('''
2782
+ [
2783
+ inet:tls:handshake=*
2784
+ :time=now
2785
+ :flow=*
2786
+ :server=$server
2787
+ :server:cert=*
2788
+ :server:fingerprint:ja3=$ja3s
2789
+ :client=$client
2790
+ :client:cert=*
2791
+ :client:fingerprint:ja3=$ja3
2792
+ ]
2793
+ ''', opts={'vars': props})
2794
+ self.len(1, nodes)
2795
+ self.nn(nodes[0].get('time'))
2796
+ self.nn(nodes[0].get('flow'))
2797
+ self.nn(nodes[0].get('server:cert'))
2798
+ self.nn(nodes[0].get('client:cert'))
2799
+
2800
+ self.eq(props['ja3'], nodes[0].get('client:fingerprint:ja3'))
2801
+ self.eq(props['ja3s'], nodes[0].get('server:fingerprint:ja3'))
2802
+
2803
+ self.eq(props['client'], nodes[0].get('client'))
2804
+ self.eq(props['server'], nodes[0].get('server'))
2805
+
2806
+ async def test_model_inet_ja3(self):
2807
+
2808
+ async with self.getTestCore() as core:
2809
+
2810
+ ja3 = '76e7b0cb0994d60a4b3f360a088fac39'
2811
+ nodes = await core.nodes('[ inet:tls:ja3:sample=(tcp://1.2.3.4, $md5) ]', opts={'vars': {'md5': ja3}})
2812
+ self.len(1, nodes)
2813
+ self.eq(nodes[0].get('client'), 'tcp://1.2.3.4')
2814
+ self.eq(nodes[0].get('ja3'), ja3)
2815
+
2816
+ ja3 = '4769ad08107979c719d86270e706fed5'
2817
+ nodes = await core.nodes('[ inet:tls:ja3s:sample=(tcp://2.2.2.2, $md5) ]', opts={'vars': {'md5': ja3}})
2818
+ self.len(1, nodes)
2819
+ self.eq(nodes[0].get('server'), 'tcp://2.2.2.2')
2820
+ self.eq(nodes[0].get('ja3s'), ja3)
2821
+
2822
+ async def test_model_inet_tls_certs(self):
2823
+
2824
+ async with self.getTestCore() as core:
2825
+
2826
+ server = 'e4f6db65dbaa7a4598f7379f75dcd5f5'
2827
+ client = 'df8d1f7e04f7c4a322e04b0b252e2851'
2828
+ nodes = await core.nodes('[inet:tls:servercert=(tcp://1.2.3.4:1234, $server)]', opts={'vars': {'server': server}})
2829
+ self.len(1, nodes)
2830
+ self.eq(nodes[0].get('server'), 'tcp://1.2.3.4:1234')
2831
+ self.eq(nodes[0].get('cert'), server)
2832
+
2833
+ nodes = await core.nodes('[inet:tls:clientcert=(tcp://5.6.7.8:5678, $client)]', opts={'vars': {'client': client}})
2834
+ self.len(1, nodes)
2835
+ self.eq(nodes[0].get('client'), 'tcp://5.6.7.8:5678')
2836
+ self.eq(nodes[0].get('cert'), client)
@@ -95,7 +95,7 @@ class InfotechModelTest(s_t_utils.SynTest):
95
95
 
96
96
  nodes = await core.nodes('''[
97
97
  it:mitre:attack:technique=T0100
98
- :name=lockpicking
98
+ :name=" LockPicking "
99
99
  :desc=speedhackers
100
100
  :url=https://locksrus.link
101
101
  :tag=cno.mitre.t0100
@@ -146,7 +146,7 @@ class InfotechModelTest(s_t_utils.SynTest):
146
146
 
147
147
  nodes = await core.nodes('''[
148
148
  it:mitre:attack:mitigation=M0100
149
- :name=patchstuff
149
+ :name=" PatchStuff "
150
150
  :desc=patchyourstuff
151
151
  :url=https://wsus.com
152
152
  :tag=cno.mitre.m0100
@@ -459,14 +459,23 @@ class OuModelTest(s_t_utils.SynTest):
459
459
  iden = await core.callStorm('ou:id:type return($node.value())')
460
460
 
461
461
  opts = {'vars': {'type': iden}}
462
- nodes = await core.nodes('[ ou:id:number=($type, visi) :status=woot :issued=202002 :expires=2021 ]', opts=opts)
462
+ nodes = await core.nodes('''
463
+ [ ou:id:number=($type, visi)
464
+ :status=woot
465
+ :issued=202002
466
+ :expires=2021
467
+ :issuer={[ ps:contact=* :name=visi ]}
468
+ ]
469
+ ''', opts=opts)
463
470
  self.len(1, nodes)
471
+ self.nn(nodes[0].get('issuer'))
464
472
  self.eq(('ou:id:number', (iden, 'visi')), nodes[0].ndef)
465
473
  self.eq(iden, nodes[0].get('type'))
466
474
  self.eq('visi', nodes[0].get('value'))
467
475
  self.eq('woot', nodes[0].get('status'))
468
476
  self.eq(1580515200000, nodes[0].get('issued'))
469
477
  self.eq(1609459200000, nodes[0].get('expires'))
478
+ self.len(1, await core.nodes('ou:id:number -> ps:contact +:name=visi'))
470
479
 
471
480
  opts = {'vars': {'type': iden}}
472
481
  nodes = await core.nodes('[ ou:id:update=* :number=($type, visi) :status=revoked :time=202003]', opts=opts)