synapse 2.197.0__py311-none-any.whl → 2.199.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 (47) hide show
  1. synapse/axon.py +3 -0
  2. synapse/common.py +3 -0
  3. synapse/cortex.py +1 -3
  4. synapse/lib/aha.py +3 -0
  5. synapse/lib/ast.py +277 -165
  6. synapse/lib/auth.py +39 -11
  7. synapse/lib/cell.py +22 -4
  8. synapse/lib/hive.py +2 -1
  9. synapse/lib/hiveauth.py +10 -1
  10. synapse/lib/jsonstor.py +6 -5
  11. synapse/lib/layer.py +6 -5
  12. synapse/lib/node.py +10 -4
  13. synapse/lib/parser.py +46 -21
  14. synapse/lib/schemas.py +13 -0
  15. synapse/lib/snap.py +112 -36
  16. synapse/lib/storm.lark +13 -11
  17. synapse/lib/storm.py +11 -10
  18. synapse/lib/storm_format.py +3 -2
  19. synapse/lib/stormtypes.py +13 -4
  20. synapse/lib/version.py +2 -2
  21. synapse/lib/view.py +2 -1
  22. synapse/models/infotech.py +18 -0
  23. synapse/models/risk.py +9 -0
  24. synapse/models/syn.py +18 -2
  25. synapse/tests/files/stormpkg/badendpoints.yaml +7 -0
  26. synapse/tests/files/stormpkg/testpkg.yaml +8 -0
  27. synapse/tests/test_cortex.py +108 -0
  28. synapse/tests/test_datamodel.py +27 -5
  29. synapse/tests/test_lib_aha.py +22 -12
  30. synapse/tests/test_lib_ast.py +57 -0
  31. synapse/tests/test_lib_auth.py +143 -2
  32. synapse/tests/test_lib_grammar.py +54 -2
  33. synapse/tests/test_lib_lmdbslab.py +24 -0
  34. synapse/tests/test_lib_storm.py +24 -0
  35. synapse/tests/test_lib_stormlib_macro.py +3 -3
  36. synapse/tests/test_lib_stormtypes.py +14 -2
  37. synapse/tests/test_model_infotech.py +13 -0
  38. synapse/tests/test_model_risk.py +6 -0
  39. synapse/tests/test_model_syn.py +58 -0
  40. synapse/tests/test_tools_genpkg.py +10 -0
  41. synapse/tests/utils.py +17 -0
  42. synapse/tools/hive/load.py +1 -0
  43. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/METADATA +1 -1
  44. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/RECORD +47 -46
  45. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/LICENSE +0 -0
  46. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/WHEEL +0 -0
  47. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/top_level.txt +0 -0
synapse/lib/snap.py CHANGED
@@ -138,10 +138,30 @@ class ProtoNode:
138
138
 
139
139
  if not await self.ctx.snap.hasNodeEdge(self.buid, verb, s_common.uhex(n2iden)):
140
140
  self.edges.add(tupl)
141
+ if len(self.edges) >= 1000:
142
+ await self.flushEdits()
141
143
  return True
142
144
 
143
145
  return False
144
146
 
147
+ async def flushEdits(self):
148
+ if (nodeedit := self.getNodeEdit()) is not None:
149
+ nodecache = {self.buid: self.node}
150
+ nodes = await self.ctx.snap.applyNodeEdits((nodeedit,), nodecache=nodecache, meta=self.ctx.meta)
151
+
152
+ if self.node is None:
153
+ if nodes and nodes[0].buid == self.buid:
154
+ self.node = nodes[0]
155
+ else: # pragma: no cover
156
+ self.node = await self.ctx.snap.getNodeByBuid(self.buid)
157
+
158
+ self.tags.clear()
159
+ self.props.clear()
160
+ self.tagprops.clear()
161
+ self.edges.clear()
162
+ self.edgedels.clear()
163
+ self.nodedata.clear()
164
+
145
165
  async def delEdge(self, verb, n2iden):
146
166
 
147
167
  if not isinstance(verb, str):
@@ -169,6 +189,8 @@ class ProtoNode:
169
189
 
170
190
  if await self.ctx.snap.layers[-1].hasNodeEdge(self.buid, verb, s_common.uhex(n2iden)):
171
191
  self.edgedels.add(tupl)
192
+ if len(self.edgedels) >= 1000:
193
+ await self.flushEdits()
172
194
  return True
173
195
 
174
196
  return False
@@ -417,6 +439,9 @@ class ProtoNode:
417
439
  full = f'{prop.name}:{subname}'
418
440
  subprop = self.form.props.get(full)
419
441
  if subprop is not None and not subprop.locked:
442
+ if subprop.deprecated:
443
+ self.ctx.snap._skipPropDeprWarn(subprop.full)
444
+
420
445
  await self.set(full, subvalu)
421
446
 
422
447
  propadds = norminfo.get('adds')
@@ -426,11 +451,14 @@ class ProtoNode:
426
451
 
427
452
  return True
428
453
 
429
- async def getSetOps(self, name, valu, norminfo=None):
454
+ async def getSetSubOps(self, name, valu, norminfo=None):
430
455
  prop = self.form.props.get(name)
431
- if prop is None:
456
+ if prop is None or prop.locked:
432
457
  return ()
433
458
 
459
+ if prop.deprecated:
460
+ self.ctx.snap._skipPropDeprWarn(prop.full)
461
+
434
462
  retn = await self._set(prop, valu, norminfo=norminfo)
435
463
  if retn is False:
436
464
  return ()
@@ -447,9 +475,7 @@ class ProtoNode:
447
475
  if propsubs is not None:
448
476
  for subname, subvalu in propsubs.items():
449
477
  full = f'{prop.name}:{subname}'
450
- subprop = self.form.props.get(full)
451
- if subprop is not None and not subprop.locked:
452
- ops.append(self.getSetOps(full, subvalu))
478
+ ops.append(self.getSetSubOps(full, subvalu))
453
479
 
454
480
  propadds = norminfo.get('adds')
455
481
  if propadds is not None:
@@ -462,7 +488,8 @@ class SnapEditor:
462
488
  '''
463
489
  A SnapEditor allows tracking node edits with subs/deps as a transaction.
464
490
  '''
465
- def __init__(self, snap):
491
+ def __init__(self, snap, meta=None):
492
+ self.meta = meta
466
493
  self.snap = snap
467
494
  self.protonodes = {}
468
495
  self.maxnodes = snap.core.maxnodes
@@ -480,6 +507,19 @@ class SnapEditor:
480
507
  nodeedits.append(nodeedit)
481
508
  return nodeedits
482
509
 
510
+ async def flushEdits(self):
511
+ nodecache = {}
512
+ nodeedits = []
513
+ for protonode in self.protonodes.values():
514
+ if (nodeedit := protonode.getNodeEdit()) is not None:
515
+ nodeedits.append(nodeedit)
516
+ nodecache[protonode.buid] = protonode.node
517
+
518
+ if nodeedits:
519
+ await self.snap.applyNodeEdits(nodeedits, nodecache=nodecache, meta=self.meta)
520
+
521
+ self.protonodes.clear()
522
+
483
523
  async def _addNode(self, form, valu, props=None, norminfo=None):
484
524
 
485
525
  self.snap.core._checkMaxNodes()
@@ -556,7 +596,7 @@ class SnapEditor:
556
596
  subs = norminfo.get('subs')
557
597
  if subs is not None:
558
598
  for prop, valu in subs.items():
559
- ops.append(protonode.getSetOps(prop, valu))
599
+ ops.append(protonode.getSetSubOps(prop, valu))
560
600
 
561
601
  adds = norminfo.get('adds')
562
602
  if adds is not None:
@@ -592,7 +632,7 @@ class SnapEditor:
592
632
  subs = norminfo.get('subs')
593
633
  if subs is not None:
594
634
  for prop, valu in subs.items():
595
- ops.append(protonode.getSetOps(prop, valu))
635
+ ops.append(protonode.getSetSubOps(prop, valu))
596
636
 
597
637
  while ops:
598
638
  oset = ops.popleft()
@@ -849,6 +889,10 @@ class Snap(s_base.Base):
849
889
  self._warnonce_keys.add(mesg)
850
890
  await self.warn(mesg, log, **info)
851
891
 
892
+ def _skipPropDeprWarn(self, name):
893
+ mesg = f'The property {name} is deprecated or using a deprecated type and will be removed in 3.0.0'
894
+ self._warnonce_keys.add(mesg)
895
+
852
896
  async def getNodeByBuid(self, buid):
853
897
  '''
854
898
  Retrieve a node tuple by binary id.
@@ -1062,6 +1106,23 @@ class Snap(s_base.Base):
1062
1106
  mesg = f'No property named "{full}".'
1063
1107
  raise s_exc.NoSuchProp(mesg=mesg)
1064
1108
 
1109
+ if isinstance(valu, dict) and isinstance(prop.type, s_types.Guid) and cmpr == '=':
1110
+ if prop.isform:
1111
+ if (node := await self._getGuidNodeByDict(prop, valu)) is not None:
1112
+ yield node
1113
+ return
1114
+
1115
+ fname = prop.type.name
1116
+ if (form := prop.modl.form(fname)) is None:
1117
+ mesg = f'The property "{full}" type "{fname}" is not a form and cannot be lifted using a dictionary.'
1118
+ raise s_exc.BadTypeValu(mesg=mesg)
1119
+
1120
+ if (node := await self._getGuidNodeByDict(form, valu)) is None:
1121
+ return
1122
+
1123
+ norm = False
1124
+ valu = node.ndef[1]
1125
+
1065
1126
  if norm:
1066
1127
  cmprvals = prop.type.getStorCmprs(cmpr, valu)
1067
1128
  # an empty return probably means ?= with invalid value
@@ -1189,11 +1250,13 @@ class Snap(s_base.Base):
1189
1250
  if nodes:
1190
1251
  return nodes[0]
1191
1252
 
1192
- async def applyNodeEdits(self, edits, nodecache=None):
1253
+ async def applyNodeEdits(self, edits, nodecache=None, meta=None):
1193
1254
  '''
1194
1255
  Sends edits to the write layer and evaluates the consequences (triggers, node object updates)
1195
1256
  '''
1196
- meta = await self.getSnapMeta()
1257
+ if meta is None:
1258
+ meta = await self.getSnapMeta()
1259
+
1197
1260
  saveoff, changes, nodes = await self._applyNodeEdits(edits, meta, nodecache=nodecache)
1198
1261
  return nodes
1199
1262
 
@@ -1398,10 +1461,6 @@ class Snap(s_base.Base):
1398
1461
 
1399
1462
  async def _addGuidNodeByDict(self, form, vals, props=None):
1400
1463
 
1401
- norms = {}
1402
- counts = []
1403
- proplist = []
1404
-
1405
1464
  if props is None:
1406
1465
  props = {}
1407
1466
 
@@ -1439,7 +1498,32 @@ class Snap(s_base.Base):
1439
1498
  raise e
1440
1499
  await self.warn(f'Skipping bad value for prop {form.name}:{name}: {mesg}')
1441
1500
 
1442
- for name, valu in vals.items():
1501
+ norms, proplist = self._normGuidNodeDict(form, vals)
1502
+
1503
+ iden = s_common.guid(proplist)
1504
+ node = await self._getGuidNodeByNorms(form, iden, norms)
1505
+
1506
+ async with self.getEditor() as editor:
1507
+
1508
+ if node is not None:
1509
+ proto = editor.loadNode(node)
1510
+ else:
1511
+ proto = await editor.addNode(form.name, iden)
1512
+ for name, (prop, valu, info) in norms.items():
1513
+ await proto.set(name, valu, norminfo=info)
1514
+
1515
+ # ensure the non-deconf props are set
1516
+ for name, (valu, info) in props.items():
1517
+ await proto.set(name, valu, norminfo=info)
1518
+
1519
+ return await self.getNodeByBuid(proto.buid)
1520
+
1521
+ def _normGuidNodeDict(self, form, props):
1522
+
1523
+ norms = {}
1524
+ proplist = []
1525
+
1526
+ for name, valu in props.items():
1443
1527
 
1444
1528
  try:
1445
1529
  prop = form.reqProp(name)
@@ -1458,22 +1542,24 @@ class Snap(s_base.Base):
1458
1542
 
1459
1543
  proplist.sort()
1460
1544
 
1545
+ return norms, proplist
1546
+
1547
+ async def _getGuidNodeByDict(self, form, props):
1548
+ norms, proplist = self._normGuidNodeDict(form, props)
1549
+ return await self._getGuidNodeByNorms(form, s_common.guid(proplist), norms)
1550
+
1551
+ async def _getGuidNodeByNorms(self, form, iden, norms):
1552
+
1461
1553
  # check first for an exact match via our same deconf strategy
1462
- iden = s_common.guid(proplist)
1463
1554
 
1464
1555
  node = await self.getNodeByNdef((form.full, iden))
1465
1556
  if node is not None:
1466
1557
 
1467
1558
  # ensure we still match the property deconf criteria
1468
- for name, (prop, norm, info) in norms.items():
1559
+ for (prop, norm, info) in norms.values():
1469
1560
  if not self._filtByPropAlts(node, prop, norm):
1470
1561
  break
1471
1562
  else:
1472
- # ensure the non-deconf props are set
1473
- async with self.getEditor() as editor:
1474
- proto = editor.loadNode(node)
1475
- for name, (valu, info) in props.items():
1476
- await proto.set(name, valu, norminfo=info)
1477
1563
  return node
1478
1564
 
1479
1565
  # TODO there is an opportunity here to populate
@@ -1482,8 +1568,10 @@ class Snap(s_base.Base):
1482
1568
  # if we lookup a node and it no longer passes the
1483
1569
  # filter...
1484
1570
 
1571
+ counts = []
1572
+
1485
1573
  # no exact match. lets do some counting.
1486
- for name, (prop, norm, info) in norms.items():
1574
+ for (prop, norm, info) in norms.values():
1487
1575
  count = await self._getPropAltCount(prop, norm)
1488
1576
  counts.append((count, prop, norm))
1489
1577
 
@@ -1499,21 +1587,9 @@ class Snap(s_base.Base):
1499
1587
  if not self._filtByPropAlts(node, prop, norm):
1500
1588
  break
1501
1589
  else:
1502
- # ensure the non-deconf props are set
1503
- async with self.getEditor() as editor:
1504
- proto = editor.loadNode(node)
1505
- for name, (valu, info) in props.items():
1506
- await proto.set(name, valu, norminfo=info)
1507
1590
  return node
1508
1591
 
1509
- async with self.getEditor() as editor:
1510
- proto = await editor.addNode(form.name, iden)
1511
- for name, (prop, valu, info) in norms.items():
1512
- await proto.set(name, valu, norminfo=info)
1513
- for name, (valu, info) in props.items():
1514
- await proto.set(name, valu, norminfo=info)
1515
-
1516
- return await self.getNodeByBuid(proto.buid)
1592
+ return None
1517
1593
 
1518
1594
  async def _getPropAltCount(self, prop, valu):
1519
1595
  count = 0
synapse/lib/storm.lark CHANGED
@@ -40,7 +40,7 @@ _editblock: "[" _editoper* "]"
40
40
  // A single edit operation
41
41
  _editoper: editnodeadd
42
42
  | editpropset | editunivset | edittagpropset | edittagadd | editcondpropset
43
- | editpropdel | editunivdel | edittagpropdel | edittagdel
43
+ | editpropsetmulti | editpropdel | editunivdel | edittagpropdel | edittagdel
44
44
  | editparens | edgeaddn1 | edgedeln1 | edgeaddn2 | edgedeln2
45
45
 
46
46
  // Parenthesis in an edit block don't have incoming nodes
@@ -48,18 +48,20 @@ editparens: "(" editnodeadd _editoper* ")"
48
48
  edittagadd: "+" [SETTAGOPER] tagname [(EQSPACE | EQNOSPACE) _valu]
49
49
  editunivdel: EXPRMINUS univprop
50
50
  edittagdel: EXPRMINUS tagname
51
- editpropset: relprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYSETPLUS | TRYSETMINUS) _valu
51
+ editpropset: relprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYMODSET) _valu
52
52
  editcondpropset: relprop condsetoper _valu
53
+ editpropsetmulti: relprop (MODSETMULTI | TRYMODSETMULTI) _valu
53
54
  editpropdel: EXPRMINUS relprop
54
- editunivset: univprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYSETPLUS | TRYSETMINUS) _valu
55
- editnodeadd: formname (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYSETPLUS | TRYSETMINUS) _valu
56
- edittagpropset: "+" tagprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYSETPLUS | TRYSETMINUS) _valu
55
+ editunivset: univprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYMODSET) _valu
56
+ editnodeadd: formname (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYMODSET) _valu
57
+ edittagpropset: "+" tagprop (EQSPACE | EQNOSPACE | MODSET | TRYSET | TRYMODSET) _valu
57
58
  edittagpropdel: EXPRMINUS tagprop
58
59
 
59
60
  EQSPACE: /((?<=\s)=|=(?=\s))/
60
61
  MODSET.4: "+=" | "-="
61
- TRYSETPLUS.1: "?+="
62
- TRYSETMINUS.1: "?-="
62
+ TRYMODSET.1: "?+=" | "?-="
63
+ MODSETMULTI.4: "++=" | "--="
64
+ TRYMODSETMULTI.1: "?++=" | "?--="
63
65
  TRYSET.1: "?="
64
66
  SETTAGOPER: "?"
65
67
 
@@ -226,10 +228,10 @@ _EDGEN2JOININIT: "<+("
226
228
  // comparisons to an expression like '<(2)'
227
229
  _EDGEN2INIT: /\<\((?=(?>(?<EDGEN2INITRECUR>\(((?>[^()"'`]+|(?&EDGEN2INITRECUR)|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'''.*?'''|'[^']*'(?!'))*)\))|'''.*?'''|`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'[^']*'(?!')|[^)])*\)[\-\+])/
228
230
 
229
- edgeaddn1: _EDGEADDN1INIT _valu _EDGEN1FINI baresubquery
230
- edgedeln1: _EDGEN1INIT _valu _EDGEN1FINI baresubquery
231
- edgeaddn2: _EDGEN2INIT _valu _EDGEADDN2FINI baresubquery
232
- edgedeln2: _EDGEN2INIT _valu _EDGEN2FINI baresubquery
231
+ edgeaddn1: _EDGEADDN1INIT _valu _EDGEN1FINI (baresubquery | _varvalu)
232
+ edgedeln1: _EDGEN1INIT _valu _EDGEN1FINI (baresubquery | _varvalu)
233
+ edgeaddn2: _EDGEN2INIT _valu _EDGEADDN2FINI (baresubquery | _varvalu)
234
+ edgedeln2: _EDGEN2INIT _valu _EDGEN2FINI (baresubquery | _varvalu)
233
235
 
234
236
  _REVERSE: /reverse(?=[\s\(])/
235
237
  liftreverse: _REVERSE "(" (liftformtag | liftpropby | liftprop | liftbyarray | lifttagtag | liftbytag | liftbytagprop | liftbyformtagprop) ")"
synapse/lib/storm.py CHANGED
@@ -1299,7 +1299,7 @@ stormcmds = (
1299
1299
 
1300
1300
  @s_cache.memoize(size=1024)
1301
1301
  def queryhash(text):
1302
- return hashlib.md5(text.encode(errors='surrogatepass'), usedforsecurity=False).hexdigest()
1302
+ return s_common.queryhash(text)
1303
1303
 
1304
1304
  class DmonManager(s_base.Base):
1305
1305
  '''
@@ -3653,23 +3653,26 @@ class MergeCmd(Cmd):
3653
3653
 
3654
3654
  genr = diffgenr()
3655
3655
 
3656
- async with await runt.snap.view.parent.snap(user=runt.user.iden) as snap:
3656
+ async with await runt.snap.view.parent.snap(user=runt.user) as snap:
3657
3657
  snap.strict = False
3658
3658
 
3659
3659
  snap.on('warn', runt.snap.dist)
3660
3660
 
3661
+ meta = {'user': runt.user.iden}
3662
+
3663
+ if doapply:
3664
+ editor = s_snap.SnapEditor(snap, meta=meta)
3665
+
3661
3666
  async for node, path in genr:
3662
3667
 
3663
3668
  # the timestamp for the adds/subs of each node merge will match
3664
3669
  nodeiden = node.iden()
3665
- meta = {'user': runt.user.iden, 'time': s_common.now()}
3670
+
3671
+ meta['time'] = s_common.now()
3666
3672
 
3667
3673
  sodes = await node.getStorNodes()
3668
3674
  sode = sodes[0]
3669
3675
 
3670
- if doapply:
3671
- editor = s_snap.SnapEditor(snap)
3672
-
3673
3676
  subs = []
3674
3677
 
3675
3678
  # check all node perms first
@@ -3818,13 +3821,11 @@ class MergeCmd(Cmd):
3818
3821
  subs.append((s_layer.EDIT_NODE_DEL, valu, ()))
3819
3822
 
3820
3823
  if doapply:
3821
- addedits = editor.getNodeEdits()
3822
- if addedits:
3823
- await runt.snap.view.parent.storNodeEdits(addedits, meta=meta)
3824
+ await editor.flushEdits()
3824
3825
 
3825
3826
  if subs:
3826
3827
  subedits = [(node.buid, node.form.name, subs)]
3827
- await runt.snap.view.storNodeEdits(subedits, meta=meta)
3828
+ await runt.snap.applyNodeEdits(subedits, nodecache={node.buid: node}, meta=meta)
3828
3829
 
3829
3830
  runt.snap.clearCachedNode(node.buid)
3830
3831
  yield await runt.snap.getNodeByBuid(node.buid), path
@@ -55,6 +55,7 @@ TerminalPygMap = {
55
55
  'LSQB': p_t.Punctuation,
56
56
  'MCASEBARE': p_t.Literal.String,
57
57
  'MODSET': p_t.Operator,
58
+ 'MODSETMULTI': p_t.Operator,
58
59
  'NONQUOTEWORD': p_t.Literal,
59
60
  'NOT': p_t.Keyword,
60
61
  'NULL': p_t.Keyword,
@@ -74,8 +75,8 @@ TerminalPygMap = {
74
75
  'TAGSEGNOVAR': p_t.Name,
75
76
  'TRIPLEQUOTEDSTRING': p_t.Literal.String,
76
77
  'TRYSET': p_t.Operator,
77
- 'TRYSETMINUS': p_t.Operator,
78
- 'TRYSETPLUS': p_t.Operator,
78
+ 'TRYMODSET': p_t.Operator,
79
+ 'TRYMODSETMULTI': p_t.Operator,
79
80
  'UNIVNAME': p_t.Name,
80
81
  'UNSET': p_t.Operator,
81
82
  'EXPRUNIVNAME': p_t.Name,
synapse/lib/stormtypes.py CHANGED
@@ -3490,7 +3490,11 @@ class LibRegx(Lib):
3490
3490
  lkey = (pattern, flags)
3491
3491
  regx = self.compiled.get(lkey)
3492
3492
  if regx is None:
3493
- regx = self.compiled[lkey] = regex.compile(pattern, flags=flags)
3493
+ try:
3494
+ regx = self.compiled[lkey] = regex.compile(pattern, flags=flags)
3495
+ except (regex.error, ValueError) as e:
3496
+ mesg = f'Error compiling regex pattern: {e}: pattern="{s_common.trimText(pattern)}"'
3497
+ raise s_exc.BadArg(mesg=mesg) from None
3494
3498
  return regx
3495
3499
 
3496
3500
  @stormfunc(readonly=True)
@@ -3500,7 +3504,12 @@ class LibRegx(Lib):
3500
3504
  pattern = await tostr(pattern)
3501
3505
  replace = await tostr(replace)
3502
3506
  regx = await self._getRegx(pattern, flags)
3503
- return regx.sub(replace, text)
3507
+
3508
+ try:
3509
+ return regx.sub(replace, text)
3510
+ except (regex.error, IndexError) as e:
3511
+ mesg = f'$lib.regex.replace() error: {e}'
3512
+ raise s_exc.BadArg(mesg=mesg) from None
3504
3513
 
3505
3514
  @stormfunc(readonly=True)
3506
3515
  async def matches(self, pattern, text, flags=0):
@@ -9868,7 +9877,7 @@ async def tostor(valu, isndef=False):
9868
9877
  retn = []
9869
9878
  for v in valu:
9870
9879
  try:
9871
- retn.append(await tostor(v))
9880
+ retn.append(await tostor(v, isndef=isndef))
9872
9881
  except s_exc.NoSuchType:
9873
9882
  pass
9874
9883
  return tuple(retn)
@@ -9877,7 +9886,7 @@ async def tostor(valu, isndef=False):
9877
9886
  retn = {}
9878
9887
  for k, v in valu.items():
9879
9888
  try:
9880
- retn[k] = await tostor(v)
9889
+ retn[k] = await tostor(v, isndef=isndef)
9881
9890
  except s_exc.NoSuchType:
9882
9891
  pass
9883
9892
  return retn
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 197, 0)
226
+ version = (2, 199, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = 'c8c758131cae47ef21e23ac8242012459ebfce9d'
228
+ commit = 'c256bd46a9df8621fd05fd6a0f36ca476ad29942'
synapse/lib/view.py CHANGED
@@ -1474,7 +1474,8 @@ class View(s_nexus.Pusher): # type: ignore
1474
1474
 
1475
1475
  meta = await snap.getSnapMeta()
1476
1476
  async for nodeedits in fromlayr.iterLayerNodeEdits():
1477
- await self.parent.storNodeEdits([nodeedits], meta)
1477
+ meta['time'] = s_common.now()
1478
+ await snap.saveNodeEdits([nodeedits], meta)
1478
1479
 
1479
1480
  async def swapLayer(self):
1480
1481
  oldlayr = self.layers[0]
@@ -1252,6 +1252,15 @@ class ItModule(s_module.CoreModule):
1252
1252
  ('product', ('it:prod:softver', {}), {
1253
1253
  'doc': 'The software which produced the log entry.'}),
1254
1254
 
1255
+ ('service:platform', ('inet:service:platform', {}), {
1256
+ 'doc': 'The service platform which generated the log event.'}),
1257
+
1258
+ ('service:instance', ('inet:service:instance', {}), {
1259
+ 'doc': 'The service instance which generated the log event.'}),
1260
+
1261
+ ('service:account', ('inet:service:account', {}), {
1262
+ 'doc': 'The service account which generated the log event.'}),
1263
+
1255
1264
  )),
1256
1265
  ('it:domain', {}, (
1257
1266
  ('name', ('str', {'lower': True, 'onespace': True}), {
@@ -2587,6 +2596,15 @@ class ItModule(s_module.CoreModule):
2587
2596
 
2588
2597
  ('synuser', ('syn:user', {}), {
2589
2598
  'doc': 'The synapse user who executed the query.'}),
2599
+
2600
+ ('service:platform', ('inet:service:platform', {}), {
2601
+ 'doc': 'The service platform which was queried.'}),
2602
+
2603
+ ('service:instance', ('inet:service:instance', {}), {
2604
+ 'doc': 'The service instance which was queried.'}),
2605
+
2606
+ ('service:account', ('inet:service:account', {}), {
2607
+ 'doc': 'The service account which ran the query.'}),
2590
2608
  )),
2591
2609
  ('it:exec:thread', {}, (
2592
2610
  ('proc', ('it:exec:proc', {}), {
synapse/models/risk.py CHANGED
@@ -810,6 +810,15 @@ class RiskModule(s_module.CoreModule):
810
810
 
811
811
  ('host', ('it:host', {}), {
812
812
  'doc': 'The host which generated the alert.'}),
813
+
814
+ ('service:platform', ('inet:service:platform', {}), {
815
+ 'doc': 'The service platform which generated the alert.'}),
816
+
817
+ ('service:instance', ('inet:service:instance', {}), {
818
+ 'doc': 'The service instance which generated the alert.'}),
819
+
820
+ ('service:account', ('inet:service:account', {}), {
821
+ 'doc': 'The service account which generated the alert.'}),
813
822
  )),
814
823
  ('risk:compromisetype', {}, ()),
815
824
  ('risk:compromise', {}, (
synapse/models/syn.py CHANGED
@@ -23,7 +23,15 @@ class SynUser(s_types.Guid):
23
23
  if user is not None:
24
24
  return user.iden, {}
25
25
 
26
- return s_types.Guid._normPyStr(self, text)
26
+ if text == '*':
27
+ mesg = f'{self.name} values must be a valid username or a guid.'
28
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text)
29
+
30
+ try:
31
+ return s_types.Guid._normPyStr(self, text)
32
+ except s_exc.BadTypeValu:
33
+ mesg = f'No user named {text} and value is not a guid.'
34
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text) from None
27
35
 
28
36
  def repr(self, iden):
29
37
 
@@ -51,7 +59,15 @@ class SynRole(s_types.Guid):
51
59
  if role is not None:
52
60
  return role.iden, {}
53
61
 
54
- return s_types.Guid._normPyStr(self, text)
62
+ if text == '*':
63
+ mesg = f'{self.name} values must be a valid rolename or a guid.'
64
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text)
65
+
66
+ try:
67
+ return s_types.Guid._normPyStr(self, text)
68
+ except s_exc.BadTypeValu:
69
+ mesg = f'No role named {text} and value is not a guid.'
70
+ raise s_exc.BadTypeValu(mesg=mesg, name=self.name, valu=text) from None
55
71
 
56
72
  def repr(self, iden):
57
73
 
@@ -0,0 +1,7 @@
1
+ name: test
2
+ version: 0.0.1
3
+ commands:
4
+ - name: 'foo.bar'
5
+ storm: (null)
6
+ endpoints:
7
+ - host: vertex.link
@@ -113,6 +113,14 @@ commands:
113
113
  - help: Help on foo opt
114
114
  - - --bar
115
115
  - help: Help on bar opt
116
+ storm: |
117
+ test:str
118
+ endpoints:
119
+ - path: /v1/test/one
120
+ - path: /v1/test/two
121
+ host: vertex.link
122
+ - path: /v1/test/three
123
+ desc: endpoint three
116
124
 
117
125
  - name: testpkg.baz
118
126
  descr: |