synapse 2.154.1__py311-none-any.whl → 2.156.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 (74) hide show
  1. synapse/cmds/cortex.py +2 -14
  2. synapse/common.py +13 -36
  3. synapse/cortex.py +15 -508
  4. synapse/lib/ast.py +215 -22
  5. synapse/lib/cell.py +35 -8
  6. synapse/lib/certdir.py +11 -0
  7. synapse/lib/cmdr.py +0 -5
  8. synapse/lib/gis.py +2 -2
  9. synapse/lib/httpapi.py +14 -43
  10. synapse/lib/layer.py +64 -201
  11. synapse/lib/lmdbslab.py +11 -0
  12. synapse/lib/node.py +1 -3
  13. synapse/lib/parser.py +10 -0
  14. synapse/lib/slabseqn.py +2 -1
  15. synapse/lib/snap.py +121 -21
  16. synapse/lib/spooled.py +9 -0
  17. synapse/lib/storm.lark +23 -6
  18. synapse/lib/storm.py +16 -339
  19. synapse/lib/storm_format.py +5 -0
  20. synapse/lib/stormhttp.py +10 -1
  21. synapse/lib/stormlib/gen.py +1 -2
  22. synapse/lib/stormlib/gis.py +41 -0
  23. synapse/lib/stormlib/graph.py +2 -1
  24. synapse/lib/stormlib/stats.py +21 -2
  25. synapse/lib/stormlib/storm.py +16 -1
  26. synapse/lib/stormtypes.py +244 -16
  27. synapse/lib/types.py +16 -2
  28. synapse/lib/version.py +2 -2
  29. synapse/lib/view.py +118 -25
  30. synapse/models/base.py +2 -2
  31. synapse/models/inet.py +60 -30
  32. synapse/models/infotech.py +130 -8
  33. synapse/models/orgs.py +3 -0
  34. synapse/models/proj.py +3 -0
  35. synapse/models/risk.py +24 -6
  36. synapse/models/syn.py +0 -38
  37. synapse/tests/test_cmds_cortex.py +1 -1
  38. synapse/tests/test_cortex.py +70 -338
  39. synapse/tests/test_lib_agenda.py +19 -54
  40. synapse/tests/test_lib_aha.py +97 -0
  41. synapse/tests/test_lib_ast.py +596 -0
  42. synapse/tests/test_lib_grammar.py +30 -10
  43. synapse/tests/test_lib_httpapi.py +33 -49
  44. synapse/tests/test_lib_layer.py +19 -234
  45. synapse/tests/test_lib_lmdbslab.py +22 -0
  46. synapse/tests/test_lib_snap.py +9 -0
  47. synapse/tests/test_lib_spooled.py +4 -0
  48. synapse/tests/test_lib_storm.py +16 -309
  49. synapse/tests/test_lib_stormlib_gis.py +21 -0
  50. synapse/tests/test_lib_stormlib_stats.py +107 -20
  51. synapse/tests/test_lib_stormlib_storm.py +25 -0
  52. synapse/tests/test_lib_stormtypes.py +253 -8
  53. synapse/tests/test_lib_types.py +40 -0
  54. synapse/tests/test_lib_view.py +6 -13
  55. synapse/tests/test_model_base.py +1 -1
  56. synapse/tests/test_model_inet.py +15 -0
  57. synapse/tests/test_model_infotech.py +110 -0
  58. synapse/tests/test_model_orgs.py +10 -0
  59. synapse/tests/test_model_person.py +0 -3
  60. synapse/tests/test_model_proj.py +2 -1
  61. synapse/tests/test_model_risk.py +24 -0
  62. synapse/tests/test_model_syn.py +20 -34
  63. synapse/tests/test_tools_csvtool.py +2 -1
  64. synapse/tests/test_tools_feed.py +4 -30
  65. synapse/tools/csvtool.py +2 -1
  66. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/METADATA +9 -9
  67. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/RECORD +70 -72
  68. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/WHEEL +1 -1
  69. synapse/cmds/cron.py +0 -726
  70. synapse/cmds/trigger.py +0 -319
  71. synapse/tests/test_cmds_cron.py +0 -453
  72. synapse/tests/test_cmds_trigger.py +0 -176
  73. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/LICENSE +0 -0
  74. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/top_level.txt +0 -0
synapse/lib/storm.lark CHANGED
@@ -63,10 +63,10 @@ SETTAGOPER: "?"
63
63
 
64
64
  // The set of non-edit non-commands in storm
65
65
 
66
- _oper: stormfunc | initblock | finiblock | trycatch | subquery | _formpivot | formjoin | formpivotin
67
- | formjoinin | opervarlist | setitem | setvar | vareval | filtoper
66
+ _oper: stormfunc | initblock | emptyblock | finiblock | trycatch | subquery | _formpivot | formjoin
67
+ | formpivotin | formjoinin | opervarlist | setitem | setvar | vareval | filtoper
68
68
  | operrelprop | forloop | whileloop | switchcase | BREAK | CONTINUE | return | emit | stop
69
- | _liftprop | ifstmt | yieldvalu | n1walk | n2walk | n1walknpivo | n2walknpivo | rawpivot
69
+ | _liftprop | ifstmt | yieldvalu | n1walk | n2walk | n1join | n2join | n1walknpivo | n2walknpivo | n1walknjoin | n2walknjoin | rawpivot
70
70
 
71
71
  BREAK.4: /break(?=[\s\}])/
72
72
  CONTINUE.4: /continue(?=[\s\}])/
@@ -106,6 +106,9 @@ yieldvalu: YIELD _argvalu
106
106
  _INIT.2: /init(?=[\s\{])/
107
107
  initblock: _INIT "{" query "}"
108
108
 
109
+ _EMPTY.2: /empty(?=[\s\{])/
110
+ emptyblock: _EMPTY "{" query "}"
111
+
109
112
  _FINI.2: /fini(?=[\s\{])/
110
113
  finiblock: _FINI "{" query "}"
111
114
 
@@ -148,7 +151,7 @@ rawpivot: _RIGHTPIVOT "{" query "}"
148
151
  _RIGHTJOIN.4: "-+>"
149
152
  _LEFTJOIN.4: "<+-"
150
153
  _RIGHTPIVOT.4: "->"
151
- _LEFTPIVOT.4: "<-"
154
+ _LEFTPIVOT.4: /<\-[^0-9]/
152
155
 
153
156
  _liftprop: liftformtag | liftpropby | liftprop | liftbyarray
154
157
  | liftbytagprop | liftbyformtagprop | liftbytag | lifttagtag
@@ -163,20 +166,33 @@ n1walk: _EDGEN1INIT (walklist | varlist | _varvalu | relpropvalu | univpropvalu
163
166
 
164
167
  n2walk: _EDGEN2INIT _valu _EDGEN2FINI _wildprops [ _cmpr _valu ]
165
168
 
169
+ n1join: _EDGEN1INIT (walklist | varlist | _varvalu | relpropvalu | univpropvalu | tagvalu | tagpropvalu | TRIPLEQUOTEDSTRING | formatstring | VARTOKN | embedquery | baresubquery | NONQUOTEWORD | ABSPROPNOUNIV) _EDGEN1JOINFINI _wildprops [ _cmpr _valu ]
170
+
171
+ n2join: _EDGEN2JOININIT _valu _EDGEN2FINI _wildprops [ _cmpr _valu ]
172
+
166
173
  walklist: ("(" (_varvalu | relpropvalu | univpropvalu | tagvalu | tagpropvalu | TRIPLEQUOTEDSTRING | formatstring | VARTOKN | NONQUOTEWORD | ABSPROP) ((",")|("," (_varvalu | relpropvalu | univpropvalu | tagvalu | tagpropvalu | TRIPLEQUOTEDSTRING | formatstring | VARTOKN | NONQUOTEWORD | ABSPROP))+ ","?) ")") -> valulist
167
174
 
175
+ _WALKNJOINN1.4: "--+>"
176
+ _WALKNJOINN2.4: "<+--"
177
+
168
178
  _WALKNPIVON1.4: "-->"
169
179
  _WALKNPIVON2.4: "<--"
170
180
 
171
181
  n1walknpivo: _WALKNPIVON1 "*"
172
182
  n2walknpivo: _WALKNPIVON2 "*"
173
183
 
184
+ n1walknjoin: _WALKNJOINN1 "*"
185
+ n2walknjoin: _WALKNJOINN2 "*"
186
+
174
187
  _EDGEADDN1INIT.2: "+("
175
188
  _EDGEN1INIT.2: "-("
176
189
  _EDGEN1FINI: ")>"
177
190
  _EDGEADDN2FINI: ")+"
178
191
  _EDGEN2FINI: ")-"
179
192
 
193
+ _EDGEN1JOINFINI: ")+>"
194
+ _EDGEN2JOININIT: "<+("
195
+
180
196
  // Regex to check for a matching ')-' or ')+' so we can avoid incorrectly matching
181
197
  // comparisons to an expression like '<(2)'
182
198
  _EDGEN2INIT: /\<\((?=(?>(?<EDGEN2INITRECUR>\(((?>[^()"'`]+|(?&EDGEN2INITRECUR)|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'''.*?'''|'[^']*'(?!'))*)\))|'''.*?'''|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'[^']*'(?!')|[^)])*\)[\-\+])/
@@ -214,7 +230,7 @@ stormcmdargs: _stormcmdarg
214
230
  _stormcmdarg: _stormcmdarg? ((CMDOPT (EQNOSPACE (argvquery | _argvalu | wordtokn))?) | argvquery | _argvalu | wordtokn)
215
231
 
216
232
  // The name of a storm command
217
- CMDNAME.2: /(?!(init|fini|function|return|emit|stop|yield|break|continue|for|while|switch|else|elif|if|not|or|and|try|catch|as|reverse)\b)[a-z][a-z0-9.]+(?=[\s\|\}]|$)/
233
+ CMDNAME.2: /(?!(init|empty|fini|function|return|emit|stop|yield|break|continue|for|while|switch|else|elif|if|not|or|and|try|catch|as|reverse)\b)[a-z][a-z0-9.]+(?=[\s\|\}]|$)/
218
234
 
219
235
  CMDOPT.4: /(?<=\s)-[a-zA-Z0-9_-]+(?![:a-zA-Z0-9_><-])/
220
236
 
@@ -258,8 +274,9 @@ _MATCHHASHWILD.3: /\#
258
274
  <\(
259
275
  (?=(?>(?<N2MATCHRECUR>\(((?>[^()"'`]+|(?&N2MATCHRECUR)|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'''.*?'''|'[^']*'(?!'))*)\))|'''.*?'''|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'[^']*'(?!')|[^)])*\)-
260
276
  )
277
+ | <(\+|-[^0-9]) # avoid matching joins or pivots
261
278
  )
262
- [@?!<>^~=]+ # match any cmprs (regex fail)
279
+ [@?!<>^~=]+ # match any cmprs (regex fail)
263
280
  )
264
281
  )
265
282
  /x
synapse/lib/storm.py CHANGED
@@ -2141,7 +2141,21 @@ class Runtime(s_base.Base):
2141
2141
 
2142
2142
  def confirm(self, perms, gateiden=None, default=None):
2143
2143
  '''
2144
- Raise AuthDeny if user doesn't have global permissions and write layer permissions
2144
+ Raise AuthDeny if the user doesn't have the permission.
2145
+
2146
+ Notes:
2147
+ An elevated runtime with asroot=True will always return True.
2148
+
2149
+ Args:
2150
+ perms (tuple): The permission tuple.
2151
+ gateiden (str): The gateiden.
2152
+ default (bool): The default value.
2153
+
2154
+ Returns:
2155
+ True: If the permission is allowed.
2156
+
2157
+ Raises:
2158
+ AuthDeny: If the user does not have the permission.
2145
2159
  '''
2146
2160
  if self.asroot:
2147
2161
  return
@@ -4598,22 +4612,6 @@ class ReIndexCmd(Cmd):
4598
4612
  if False:
4599
4613
  yield
4600
4614
 
4601
- class SudoCmd(Cmd):
4602
- '''
4603
- Deprecated sudo command.
4604
-
4605
- Left in for 2.x.x so that Storm command with it are still valid to execute.
4606
- '''
4607
- name = 'sudo'
4608
-
4609
- async def execStormCmd(self, runt, genr):
4610
-
4611
- s_common.deprdate('storm command: sudo', s_common._splicedepr)
4612
-
4613
- await runt.snap.warn(f'sudo command is deprecated and will be removed on {s_common._splicedepr}')
4614
- async for node, path in genr:
4615
- yield node, path
4616
-
4617
4615
  class MoveTagCmd(Cmd):
4618
4616
  '''
4619
4617
  Rename an entire tag tree and preserve time intervals.
@@ -4881,7 +4879,7 @@ class GraphCmd(Cmd):
4881
4879
  inet:fqdn | graph
4882
4880
  --degrees 2
4883
4881
  --filter { -#nope }
4884
- --pivot { <- meta:seen <- meta:source }
4882
+ --pivot { -> meta:seen }
4885
4883
  --form-pivot inet:fqdn {<- * | limit 20}
4886
4884
  --form-pivot inet:fqdn {-> * | limit 20}
4887
4885
  --form-filter inet:fqdn {-inet:fqdn:issuffix=1}
@@ -5463,327 +5461,6 @@ class ScrapeCmd(Cmd):
5463
5461
  if self.opts.doyield:
5464
5462
  yield addnode, runt.initPath(addnode)
5465
5463
 
5466
- class SpliceListCmd(Cmd):
5467
- '''
5468
- Deprecated command to retrieve a list of splices backwards from the end of the splicelog.
5469
-
5470
- Examples:
5471
-
5472
- # Show the last 10 splices.
5473
- splice.list | limit 10
5474
-
5475
- # Show splices after a specific time.
5476
- splice.list --mintime "2020/01/06 15:38:10.991"
5477
-
5478
- # Show splices from a specific timeframe.
5479
- splice.list --mintimestamp 1578422719360 --maxtimestamp 1578422719367
5480
-
5481
- Notes:
5482
-
5483
- If both a time string and timestamp value are provided for a min or max,
5484
- the timestamp will take precedence over the time string value.
5485
- '''
5486
-
5487
- name = 'splice.list'
5488
- readonly = True
5489
-
5490
- def getArgParser(self):
5491
- pars = Cmd.getArgParser(self)
5492
-
5493
- pars.add_argument('--maxtimestamp', type='int', default=None,
5494
- help='Only yield splices which occurred on or before this timestamp.')
5495
- pars.add_argument('--mintimestamp', type='int', default=None,
5496
- help='Only yield splices which occurred on or after this timestamp.')
5497
- pars.add_argument('--maxtime', type='str', default=None,
5498
- help='Only yield splices which occurred on or before this time.')
5499
- pars.add_argument('--mintime', type='str', default=None,
5500
- help='Only yield splices which occurred on or after this time.')
5501
-
5502
- return pars
5503
-
5504
- async def execStormCmd(self, runt, genr):
5505
-
5506
- s_common.deprdate('storm command: splice.list', s_common._splicedepr)
5507
-
5508
- mesg = f'splice.list is deprecated and will be removed on {s_common._splicedepr}'
5509
- await runt.snap.warn(mesg)
5510
-
5511
- maxtime = None
5512
- if self.opts.maxtimestamp:
5513
- maxtime = self.opts.maxtimestamp
5514
- elif self.opts.maxtime:
5515
- try:
5516
- maxtime = s_time.parse(self.opts.maxtime, chop=True)
5517
- except s_exc.BadTypeValu as e:
5518
- mesg = f'Error during maxtime parsing - {str(e)}'
5519
-
5520
- raise s_exc.StormRuntimeError(mesg=mesg, valu=self.opts.maxtime) from None
5521
-
5522
- mintime = None
5523
- if self.opts.mintimestamp:
5524
- mintime = self.opts.mintimestamp
5525
- elif self.opts.mintime:
5526
- try:
5527
- mintime = s_time.parse(self.opts.mintime, chop=True)
5528
- except s_exc.BadTypeValu as e:
5529
- mesg = f'Error during mintime parsing - {str(e)}'
5530
-
5531
- raise s_exc.StormRuntimeError(mesg=mesg, valu=self.opts.mintime) from None
5532
-
5533
- i = 0
5534
-
5535
- async for splice in runt.snap.core.spliceHistory(runt.user):
5536
-
5537
- splicetime = splice[1].get('time')
5538
- if splicetime is None:
5539
- splicetime = 0
5540
-
5541
- if maxtime and maxtime < splicetime:
5542
- continue
5543
-
5544
- if mintime and mintime > splicetime:
5545
- return
5546
-
5547
- guid = s_common.guid(splice)
5548
-
5549
- buid = s_common.buid(splice[1]['ndef'])
5550
- iden = s_common.ehex(buid)
5551
-
5552
- props = {'.created': s_common.now(),
5553
- 'splice': splice,
5554
- 'type': splice[0],
5555
- 'iden': iden,
5556
- 'form': splice[1]['ndef'][0],
5557
- 'time': splicetime,
5558
- 'user': splice[1].get('user'),
5559
- 'prov': splice[1].get('prov'),
5560
- }
5561
-
5562
- prop = splice[1].get('prop')
5563
- if prop:
5564
- props['prop'] = prop
5565
-
5566
- tag = splice[1].get('tag')
5567
- if tag:
5568
- props['tag'] = tag
5569
-
5570
- if 'valu' in splice[1]:
5571
- props['valu'] = splice[1]['valu']
5572
- elif splice[0] == 'node:del':
5573
- props['valu'] = splice[1]['ndef'][1]
5574
-
5575
- if 'oldv' in splice[1]:
5576
- props['oldv'] = splice[1]['oldv']
5577
-
5578
- fullnode = (buid, {
5579
- 'ndef': ('syn:splice', guid),
5580
- 'tags': {},
5581
- 'props': props,
5582
- 'tagprops': {},
5583
- })
5584
-
5585
- node = s_node.Node(runt.snap, fullnode)
5586
-
5587
- yield (node, runt.initPath(node))
5588
-
5589
- i += 1
5590
- # Yield to other tasks occasionally
5591
- if not i % 1000:
5592
- await asyncio.sleep(0)
5593
-
5594
- class SpliceUndoCmd(Cmd):
5595
- '''
5596
- Deprecated command to reverse the actions of syn:splice runt nodes.
5597
-
5598
- Examples:
5599
-
5600
- # Undo the last 5 splices.
5601
- splice.list | limit 5 | splice.undo
5602
-
5603
- # Undo splices after a specific time.
5604
- splice.list --mintime "2020/01/06 15:38:10.991" | splice.undo
5605
-
5606
- # Undo splices from a specific timeframe.
5607
- splice.list --mintimestamp 1578422719360 --maxtimestamp 1578422719367 | splice.undo
5608
- '''
5609
-
5610
- name = 'splice.undo'
5611
-
5612
- def __init__(self, runt, runtsafe):
5613
- self.undo = {
5614
- 'prop:set': self.undoPropSet,
5615
- 'prop:del': self.undoPropDel,
5616
- 'node:add': self.undoNodeAdd,
5617
- 'node:del': self.undoNodeDel,
5618
- 'tag:add': self.undoTagAdd,
5619
- 'tag:del': self.undoTagDel,
5620
- 'tag:prop:set': self.undoTagPropSet,
5621
- 'tag:prop:del': self.undoTagPropDel,
5622
- }
5623
- Cmd.__init__(self, runt, runtsafe)
5624
-
5625
- def getArgParser(self):
5626
- pars = Cmd.getArgParser(self)
5627
- forcehelp = 'Force delete nodes even if it causes broken references (requires admin).'
5628
- pars.add_argument('--force', default=False, action='store_true', help=forcehelp)
5629
- return pars
5630
-
5631
- async def undoPropSet(self, runt, splice, node):
5632
-
5633
- name = splice.props.get('prop')
5634
- if name == '.created':
5635
- return
5636
-
5637
- if node:
5638
- prop = node.form.props.get(name)
5639
- if prop is None: # pragma: no cover
5640
- mesg = f'No property named {name}.'
5641
- raise s_exc.NoSuchProp(mesg=mesg, name=name, form=node.form.name)
5642
-
5643
- oldv = splice.props.get('oldv')
5644
- if oldv is not None:
5645
- runt.layerConfirm(('node', 'prop', 'set', prop.full))
5646
- await node.set(name, oldv)
5647
- else:
5648
- runt.layerConfirm(('node', 'prop', 'del', prop.full))
5649
- await node.pop(name)
5650
-
5651
- async def undoPropDel(self, runt, splice, node):
5652
-
5653
- name = splice.props.get('prop')
5654
- if name == '.created':
5655
- return
5656
-
5657
- if node:
5658
- prop = node.form.props.get(name)
5659
- if prop is None: # pragma: no cover
5660
- mesg = f'No property named {name}.'
5661
- raise s_exc.NoSuchProp(mesg=mesg, name=name, form=node.form.name)
5662
-
5663
- valu = splice.props.get('valu')
5664
-
5665
- runt.layerConfirm(('node', 'prop', 'set', prop.full))
5666
- await node.set(name, valu)
5667
-
5668
- async def undoNodeAdd(self, runt, splice, node):
5669
-
5670
- if node:
5671
- for tag in node.tags.keys():
5672
- runt.layerConfirm(('node', 'tag', 'del', *tag.split('.')))
5673
-
5674
- runt.layerConfirm(('node', 'del', node.form.name))
5675
- await node.delete(force=self.opts.force)
5676
-
5677
- async def undoNodeDel(self, runt, splice, node):
5678
-
5679
- if node is None:
5680
- form = splice.props.get('form')
5681
- valu = splice.props.get('valu')
5682
-
5683
- if form and (valu is not None):
5684
- runt.layerConfirm(('node', 'add', form))
5685
- await runt.snap.addNode(form, valu)
5686
-
5687
- async def undoTagAdd(self, runt, splice, node):
5688
-
5689
- if node:
5690
- tag = splice.props.get('tag')
5691
- parts = tag.split('.')
5692
- runt.layerConfirm(('node', 'tag', 'del', *parts))
5693
-
5694
- await node.delTag(tag)
5695
-
5696
- oldv = splice.props.get('oldv')
5697
- if oldv is not None:
5698
- runt.layerConfirm(('node', 'tag', 'add', *parts))
5699
- await node.addTag(tag, valu=oldv)
5700
-
5701
- async def undoTagDel(self, runt, splice, node):
5702
-
5703
- if node:
5704
- tag = splice.props.get('tag')
5705
- parts = tag.split('.')
5706
- runt.layerConfirm(('node', 'tag', 'add', *parts))
5707
-
5708
- valu = splice.props.get('valu')
5709
- if valu is not None:
5710
- await node.addTag(tag, valu=valu)
5711
-
5712
- async def undoTagPropSet(self, runt, splice, node):
5713
-
5714
- if node:
5715
- tag = splice.props.get('tag')
5716
- parts = tag.split('.')
5717
-
5718
- prop = splice.props.get('prop')
5719
-
5720
- oldv = splice.props.get('oldv')
5721
- if oldv is not None:
5722
- runt.layerConfirm(('node', 'tag', 'add', *parts))
5723
- await node.setTagProp(tag, prop, oldv)
5724
- else:
5725
- runt.layerConfirm(('node', 'tag', 'del', *parts))
5726
- await node.delTagProp(tag, prop)
5727
-
5728
- async def undoTagPropDel(self, runt, splice, node):
5729
-
5730
- if node:
5731
- tag = splice.props.get('tag')
5732
- parts = tag.split('.')
5733
- runt.layerConfirm(('node', 'tag', 'add', *parts))
5734
-
5735
- prop = splice.props.get('prop')
5736
-
5737
- valu = splice.props.get('valu')
5738
- if valu is not None:
5739
- await node.setTagProp(tag, prop, valu)
5740
-
5741
- async def execStormCmd(self, runt, genr):
5742
-
5743
- s_common.deprdate('storm command: splice.undo', s_common._splicedepr)
5744
-
5745
- mesg = f'splice.undo is deprecated and will be removed on {s_common._splicedepr}'
5746
- await runt.snap.warn(mesg)
5747
-
5748
- if self.opts.force:
5749
- if not runt.user.isAdmin():
5750
- mesg = '--force requires admin privs.'
5751
- raise s_exc.AuthDeny(mesg=mesg)
5752
-
5753
- i = 0
5754
-
5755
- async for node, path in genr:
5756
-
5757
- if not node.form.name == 'syn:splice':
5758
- mesg = 'splice.undo only accepts syn:splice nodes'
5759
- raise s_exc.StormRuntimeError(mesg=mesg, form=node.form.name)
5760
-
5761
- if False: # make this method an async generator function
5762
- yield None
5763
-
5764
- splicetype = node.props.get('type')
5765
-
5766
- if splicetype in self.undo:
5767
-
5768
- iden = node.props.get('iden')
5769
- if iden is None:
5770
- continue
5771
-
5772
- buid = s_common.uhex(iden)
5773
- if len(buid) != 32:
5774
- raise s_exc.NoSuchIden(mesg='Iden must be 32 bytes', iden=iden)
5775
-
5776
- splicednode = await runt.snap.getNodeByBuid(buid)
5777
-
5778
- await self.undo[splicetype](runt, node, splicednode)
5779
- else:
5780
- raise s_exc.StormRuntimeError(mesg='Unknown splice type.', splicetype=splicetype)
5781
-
5782
- i += 1
5783
- # Yield to other tasks occasionally
5784
- if not i % 1000:
5785
- await asyncio.sleep(0)
5786
-
5787
5464
  class LiftByVerb(Cmd):
5788
5465
  '''
5789
5466
  Lift nodes from the current view by an light edge verb.
@@ -95,9 +95,12 @@ TerminalPygMap = {
95
95
  '_EDGEN1INIT': p_t.Punctuation,
96
96
  '_EDGEN2INIT': p_t.Punctuation,
97
97
  '_EDGEN2FINI': p_t.Punctuation,
98
+ '_EDGEN1JOINFINI': p_t.Punctuation,
99
+ '_EDGEN2JOININIT': p_t.Punctuation,
98
100
  '_ELSE': p_t.Keyword,
99
101
  '_EMBEDQUERYSTART': p_t.Punctuation,
100
102
  '_EMIT': p_t.Keyword,
103
+ '_EMPTY': p_t.Keyword,
101
104
  '_EXPRCOLONNOSPACE': p_t.Punctuation,
102
105
  '_FINI': p_t.Keyword,
103
106
  '_HASH': p_t.Punctuation,
@@ -113,6 +116,8 @@ TerminalPygMap = {
113
116
  '_RIGHTJOIN': p_t.Punctuation,
114
117
  '_RIGHTPIVOT': p_t.Punctuation,
115
118
  '_STOP': p_t.Keyword,
119
+ '_WALKNJOINN1': p_t.Punctuation,
120
+ '_WALKNJOINN2': p_t.Punctuation,
116
121
  '_WALKNPIVON1': p_t.Punctuation,
117
122
  '_WALKNPIVON2': p_t.Punctuation,
118
123
  '$END': p_t.Punctuation,
synapse/lib/stormhttp.py CHANGED
@@ -417,7 +417,16 @@ class LibHttp(s_stormtypes.Lib):
417
417
  data = self._buildFormData(fields)
418
418
  else:
419
419
  data = body
420
- async with sess.request(meth, url, headers=headers, json=json, data=data, **kwargs) as resp:
420
+
421
+ # `data` and `json` are passed in kwargs only if they are not
422
+ # None because of a weird interaction with aiohttp and vcrpy.
423
+ if data is not None:
424
+ kwargs['data'] = data
425
+
426
+ if json is not None:
427
+ kwargs['json'] = json
428
+
429
+ async with sess.request(meth, url, headers=headers, **kwargs) as resp:
421
430
  info = {
422
431
  'code': resp.status,
423
432
  'reason': await self.codereason(resp.status),
@@ -467,8 +467,7 @@ stormcmds = (
467
467
  {
468
468
  'name': 'gen.pol.country.government',
469
469
  'descr': '''
470
- Lift (or create) the ou:org node representing a country's
471
- government based on the 2 letter ISO-3166 country code.
470
+ Lift (or create) the ou:org node representing a country's government based on the 2 letter ISO-3166 country code.
472
471
 
473
472
  Examples:
474
473
 
@@ -0,0 +1,41 @@
1
+ import synapse.exc as s_exc
2
+
3
+ import synapse.lib.gis as s_gis
4
+ import synapse.lib.stormtypes as s_stormtypes
5
+
6
+ @s_stormtypes.registry.registerLib
7
+ class GisLib(s_stormtypes.Lib):
8
+ '''
9
+ A Storm library which implements helpers for earth based geospatial calculations.
10
+ '''
11
+ _storm_locals = (
12
+ {'name': 'bbox', 'desc': 'Calculate a min/max bounding box for the specified circle.',
13
+ 'type': {'type': 'function', '_funcname': '_methBbox',
14
+ 'args': (
15
+ {'name': 'lon', 'type': 'float', 'desc': 'The longitude in degrees.'},
16
+ {'name': 'lat', 'type': 'float', 'desc': 'The latitude in degrees.'},
17
+ {'name': 'dist', 'type': 'int', 'desc': 'A distance in geo:dist base units (mm).'},
18
+ ),
19
+ 'returns': {'type': 'list', 'desc': 'A tuple of (lonmin, lonmax, latmin, latmax).', }
20
+ }},
21
+ )
22
+ _storm_lib_path = ('gis',)
23
+
24
+ def getObjLocals(self):
25
+ return {
26
+ 'bbox': self._methBbox,
27
+ }
28
+
29
+ @s_stormtypes.stormfunc(readonly=True)
30
+ async def _methBbox(self, lon, lat, dist):
31
+ try:
32
+ lon = float(await s_stormtypes.toprim(lon))
33
+ lat = float(await s_stormtypes.toprim(lat))
34
+ dist = await s_stormtypes.toint(dist)
35
+ except ValueError as e:
36
+ raise s_exc.BadArg(mesg=f'$lib.gis.bbox(): {e}')
37
+ except s_exc.BadCast as e:
38
+ raise s_exc.BadArg(mesg=f'$lib.gis.bbox(): {e.get("mesg")}')
39
+
40
+ (latmin, latmax, lonmin, lonmax) = s_gis.bbox(lat, lon, dist)
41
+ return (lonmin, lonmax, latmin, latmax)
@@ -20,6 +20,7 @@ gdefSchema = {
20
20
  'updated': {'type': 'number'},
21
21
  'refs': {'type': 'boolean', 'default': False},
22
22
  'edges': {'type': 'boolean', 'default': True},
23
+ 'edgelimit': {'type': 'number', 'default': 3000},
23
24
  'degrees': {'type': ['integer', 'null'], 'minimum': 0},
24
25
  'filterinput': {'type': 'boolean', 'default': True},
25
26
  'yieldfiltered': {'type': 'boolean', 'default': False},
@@ -96,7 +97,7 @@ class GraphLib(s_stormtypes.Lib):
96
97
  "name": "Test Projection",
97
98
  "desc": "My test projection",
98
99
  "degrees": 2,
99
- "pivots": ["<- meta:seen <- meta:source"],
100
+ "pivots": ["-> meta:seen"],
100
101
  "filters": ["-#nope"],
101
102
  "forms": {
102
103
  "inet:fqdn": {
@@ -41,6 +41,8 @@ class StatsCountByCmd(s_storm.Cmd):
41
41
  help='Maximum width of the labels to display.')
42
42
  pars.add_argument('--yield', default=False, action='store_true',
43
43
  dest='yieldnodes', help='Yield inbound nodes.')
44
+ pars.add_argument('--by-name', default=False, action='store_true',
45
+ help='Print stats sorted by name instead of count.')
44
46
  return pars
45
47
 
46
48
  async def execStormCmd(self, runt, genr):
@@ -55,6 +57,8 @@ class StatsCountByCmd(s_storm.Cmd):
55
57
  mesg = f'Value for --bar-width must be >= 0, got: {barwidth}'
56
58
  raise s_exc.BadArg(mesg=mesg)
57
59
 
60
+ byname = await s_stormtypes.tobool(self.opts.by_name)
61
+
58
62
  counts = collections.defaultdict(int)
59
63
 
60
64
  usenode = self.opts.valu is s_common.novalu
@@ -78,9 +82,24 @@ class StatsCountByCmd(s_storm.Cmd):
78
82
  await runt.printf('No values to display!')
79
83
  return
80
84
 
81
- values = list(sorted(counts.items(), key=lambda x: x[1]))
85
+ if byname:
86
+ # Try to sort numerically instead of lexicographically
87
+ def coerce(indx):
88
+ def wrapped(valu):
89
+ valu = valu[indx]
90
+ try:
91
+ return int(valu)
92
+ except ValueError:
93
+ return valu
94
+ return wrapped
95
+
96
+ values = list(sorted(counts.items(), key=coerce(0)))
97
+ maxv = max(val[1] for val in values)
98
+
99
+ else:
100
+ values = list(sorted(counts.items(), key=lambda x: x[1]))
101
+ maxv = values[-1][1]
82
102
 
83
- maxv = values[-1][1]
84
103
  size = await s_stormtypes.toint(self.opts.size, noneok=True)
85
104
  char = (await s_stormtypes.tostr(self.opts.char))[0]
86
105
  reverse = self.opts.reverse
@@ -1,14 +1,25 @@
1
+ import logging
2
+
1
3
  import synapse.exc as s_exc
2
4
 
3
5
  import synapse.lib.stormtypes as s_stormtypes
4
6
 
7
+ evaldesc = '''\
8
+ Evaluate a storm runtime value and optionally cast/coerce it.
9
+
10
+ NOTE: If storm logging is enabled, the expression being evaluated will be logged
11
+ separately.
12
+ '''
13
+
14
+ stormlogger = logging.getLogger('synapse.storm')
15
+
5
16
  @s_stormtypes.registry.registerLib
6
17
  class LibStorm(s_stormtypes.Lib):
7
18
  '''
8
19
  A Storm library for evaluating dynamic storm expressions.
9
20
  '''
10
21
  _storm_locals = (
11
- {'name': 'eval', 'desc': 'Evaluate a storm runtime value and optionally cast/coerce it.',
22
+ {'name': 'eval', 'desc': evaldesc,
12
23
  'type': {'type': 'function', '_funcname': '_evalStorm',
13
24
  'args': (
14
25
  {'name': 'text', 'type': 'str', 'desc': 'A storm expression string.'},
@@ -29,6 +40,10 @@ class LibStorm(s_stormtypes.Lib):
29
40
  text = await s_stormtypes.tostr(text)
30
41
  cast = await s_stormtypes.tostr(cast, noneok=True)
31
42
 
43
+ if self.runt.snap.core.stormlog:
44
+ extra = await self.runt.snap.core.getLogExtra(text=text)
45
+ stormlogger.info(f'Executing storm query via $lib.storm.eval() {{{text}}} as [{self.runt.user.name}]', extra=extra)
46
+
32
47
  casttype = None
33
48
  if cast:
34
49