synapse 2.219.0__py311-none-any.whl → 2.221.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 (55) hide show
  1. synapse/data/__init__.py +4 -0
  2. synapse/data/lark/__init__.py +0 -0
  3. synapse/data/lark/imap.lark +8 -0
  4. synapse/exc.py +2 -0
  5. synapse/lib/json.py +6 -5
  6. synapse/lib/layer.py +134 -0
  7. synapse/lib/link.py +49 -50
  8. synapse/lib/parser.py +3 -5
  9. synapse/lib/rstorm.py +65 -2
  10. synapse/lib/snap.py +21 -13
  11. synapse/lib/storm.py +1 -0
  12. synapse/lib/stormhttp.py +10 -10
  13. synapse/lib/stormlib/aha.py +3 -3
  14. synapse/lib/stormlib/auth.py +11 -11
  15. synapse/lib/stormlib/cell.py +1 -1
  16. synapse/lib/stormlib/cortex.py +9 -9
  17. synapse/lib/stormlib/env.py +4 -5
  18. synapse/lib/stormlib/ethereum.py +1 -1
  19. synapse/lib/stormlib/gen.py +3 -3
  20. synapse/lib/stormlib/hex.py +2 -2
  21. synapse/lib/stormlib/imap.py +478 -37
  22. synapse/lib/stormlib/infosec.py +2 -2
  23. synapse/lib/stormlib/iters.py +2 -2
  24. synapse/lib/stormlib/model.py +5 -5
  25. synapse/lib/stormlib/notifications.py +1 -1
  26. synapse/lib/stormlib/oauth.py +2 -2
  27. synapse/lib/stormlib/project.py +3 -3
  28. synapse/lib/stormlib/scrape.py +2 -1
  29. synapse/lib/stormlib/smtp.py +3 -3
  30. synapse/lib/stormlib/stats.py +2 -2
  31. synapse/lib/stormlib/stix.py +2 -2
  32. synapse/lib/stormlib/utils.py +19 -0
  33. synapse/lib/stormlib/vault.py +1 -1
  34. synapse/lib/stormlib/xml.py +2 -2
  35. synapse/lib/stormlib/yaml.py +1 -1
  36. synapse/lib/stormtypes.py +203 -60
  37. synapse/lib/version.py +2 -2
  38. synapse/tests/test_lib_grammar.py +2 -4
  39. synapse/tests/test_lib_json.py +29 -0
  40. synapse/tests/test_lib_layer.py +86 -67
  41. synapse/tests/test_lib_rstorm.py +132 -0
  42. synapse/tests/test_lib_storm.py +11 -1
  43. synapse/tests/test_lib_stormlib_env.py +3 -1
  44. synapse/tests/test_lib_stormlib_imap.py +1307 -230
  45. synapse/tests/test_lib_stormlib_utils.py +10 -0
  46. synapse/tests/test_lib_stormtypes.py +583 -2
  47. synapse/tools/aha/list.py +9 -9
  48. synapse/tools/aha/provision/service.py +2 -2
  49. synapse/utils/stormcov/plugin.py +2 -5
  50. {synapse-2.219.0.dist-info → synapse-2.221.0.dist-info}/METADATA +1 -2
  51. {synapse-2.219.0.dist-info → synapse-2.221.0.dist-info}/RECORD +55 -53
  52. /synapse/{lib → data/lark}/storm.lark +0 -0
  53. {synapse-2.219.0.dist-info → synapse-2.221.0.dist-info}/WHEEL +0 -0
  54. {synapse-2.219.0.dist-info → synapse-2.221.0.dist-info}/licenses/LICENSE +0 -0
  55. {synapse-2.219.0.dist-info → synapse-2.221.0.dist-info}/top_level.txt +0 -0
synapse/lib/stormtypes.py CHANGED
@@ -312,6 +312,18 @@ class StormTypesRegistry:
312
312
  locl = getattr(obj, funcname, None)
313
313
  assert locl is not None, f'bad _funcname=[{funcname}] for {obj} {info.get("name")}'
314
314
  args = rtype.get('args', ())
315
+ for idx, arg in enumerate(args):
316
+ argname = arg.get('name')
317
+ assert argname is not None, f'Argument at index {idx} has no name'
318
+ argtype = arg.get('type')
319
+ assert argtype is not None, f'The type for argument {argname} of function {funcname} is unknown'
320
+ if isinstance(argtype, (list, tuple)):
321
+ for atyp in argtype:
322
+ if atyp not in self.known_types and atyp not in self.undefined_types:
323
+ raise s_exc.NoSuchType(mesg=f'The argument type {atyp} for arg {argname} of function {obj.__name__}.{funcname} is unknown.', type=argtype)
324
+ else:
325
+ if argtype not in self.known_types and argtype not in self.undefined_types:
326
+ raise s_exc.NoSuchType(mesg=f'The argument type {argtype} for arg {argname} of function {obj.__name__}.{funcname} is unknown.', type=argtype)
315
327
  callsig = getCallSig(locl)
316
328
  # Assert the callsigs match
317
329
  callsig_args = [str(v).split('=')[0] for v in callsig.parameters.values()]
@@ -1454,7 +1466,7 @@ class LibBase(Lib):
1454
1466
  {'name': 'valu', 'type': 'any', 'desc': 'The value to normalize.', },
1455
1467
  ),
1456
1468
  'returns': {'type': 'list',
1457
- 'desc': 'A list of (<bool>, <prim>) for status and normalized value.', }}},
1469
+ 'desc': 'A list of (<boolean>, <prim>) for status and normalized value.', }}},
1458
1470
  {'name': 'repr', 'desc': '''
1459
1471
  Attempt to convert a system mode value to a display mode string.
1460
1472
 
@@ -2159,7 +2171,7 @@ class LibAxon(Lib):
2159
2171
  'desc': 'Set to False to disable SSL/TLS certificate verification.', 'default': True},
2160
2172
  {'name': 'timeout', 'type': 'int', 'desc': 'Timeout for the download operation.',
2161
2173
  'default': None},
2162
- {'name': 'proxy', 'type': ['bool', 'str'],
2174
+ {'name': 'proxy', 'type': ['boolean', 'str'],
2163
2175
  'desc': 'Configure proxy usage. See $lib.axon help for additional details.', 'default': True},
2164
2176
  {'name': 'ssl_opts', 'type': 'dict',
2165
2177
  'desc': 'Optional SSL/TLS options. See $lib.axon help for additional details.',
@@ -2182,7 +2194,7 @@ class LibAxon(Lib):
2182
2194
  'desc': 'Set to False to disable SSL/TLS certificate verification.', 'default': True},
2183
2195
  {'name': 'timeout', 'type': 'int', 'desc': 'Timeout for the download operation.',
2184
2196
  'default': None},
2185
- {'name': 'proxy', 'type': ['bool', 'str'],
2197
+ {'name': 'proxy', 'type': ['boolean', 'str'],
2186
2198
  'desc': 'Configure proxy usage. See $lib.axon help for additional details.', 'default': True},
2187
2199
  {'name': 'ssl_opts', 'type': 'dict',
2188
2200
  'desc': 'Optional SSL/TLS options. See $lib.axon help for additional details.',
@@ -2212,7 +2224,7 @@ class LibAxon(Lib):
2212
2224
  ''',
2213
2225
  'type': {'type': 'function', '_funcname': 'del_',
2214
2226
  'args': (
2215
- {'name': 'sha256', 'type': 'hash:sha256',
2227
+ {'name': 'sha256', 'type': 'str',
2216
2228
  'desc': 'The sha256 of the bytes to remove from the Axon.'},
2217
2229
  ),
2218
2230
  'returns': {'type': 'boolean', 'desc': 'True if the bytes were found and removed.'}}},
@@ -4967,6 +4979,24 @@ class Bytes(Prim):
4967
4979
  {'name': 'offset', 'type': 'int', 'desc': 'An offset to begin unpacking from.', 'default': 0},
4968
4980
  ),
4969
4981
  'returns': {'type': 'list', 'desc': 'The unpacked primitive values.', }}},
4982
+ {'name': 'xor', 'desc': '''
4983
+ Perform an "exclusive or" bitwise operation on the bytes and another set of bytes.
4984
+
4985
+ Notes:
4986
+ The key bytes provided as an argument will be repeated as needed until all bytes have been
4987
+ xor'd.
4988
+
4989
+ If a string is provided as the key argument, it will be utf8 encoded before being xor'd.
4990
+
4991
+ Examples:
4992
+ Perform an xor operation on the bytes in $encoded using the bytes in $key::
4993
+
4994
+ $decoded = $encoded.xor($key)''',
4995
+ 'type': {'type': 'function', '_funcname': '_methXor',
4996
+ 'args': (
4997
+ {'name': 'key', 'type': ['str', 'bytes'], 'desc': 'The key bytes to perform the xor operation with.'},
4998
+ ),
4999
+ 'returns': {'type': 'bytes', 'desc': "The xor'd bytes."}}},
4970
5000
  )
4971
5001
  _storm_typename = 'bytes'
4972
5002
  _ismutable = False
@@ -4977,6 +5007,7 @@ class Bytes(Prim):
4977
5007
 
4978
5008
  def getObjLocals(self):
4979
5009
  return {
5010
+ 'xor': self._methXor,
4980
5011
  'decode': self._methDecode,
4981
5012
  'bunzip': self._methBunzip,
4982
5013
  'gunzip': self._methGunzip,
@@ -5065,6 +5096,26 @@ class Bytes(Prim):
5065
5096
  except UnicodeDecodeError as e:
5066
5097
  raise s_exc.StormRuntimeError(mesg=f'{e}: {s_common.trimText(repr(valu))}') from None
5067
5098
 
5099
+ @stormfunc(readonly=True)
5100
+ async def _methXor(self, key):
5101
+ key = await toprim(key)
5102
+ if isinstance(key, str):
5103
+ key = key.encode()
5104
+
5105
+ if not isinstance(key, bytes):
5106
+ raise s_exc.BadArg(mesg='$bytes.xor() key argument must be bytes or a str.')
5107
+
5108
+ if len(key) == 0:
5109
+ raise s_exc.BadArg(mesg='$bytes.xor() key length must be greater than 0.')
5110
+
5111
+ arry = bytearray(self.valu)
5112
+ keylen = len(key)
5113
+
5114
+ for i in range(len(arry)):
5115
+ arry[i] ^= key[i % keylen]
5116
+
5117
+ return bytes(arry)
5118
+
5068
5119
  @registry.registerType
5069
5120
  class Dict(Prim):
5070
5121
  '''
@@ -7139,11 +7190,11 @@ class Layer(Prim):
7139
7190
  'type': {'type': 'function', '_funcname': 'getStorNodesByProp',
7140
7191
  'args': (
7141
7192
  {'name': 'propname', 'type': 'str', 'desc': 'The full property name to lift by.'},
7142
- {'name': 'propvalu', 'type': 'obj', 'desc': 'The value for the property.', 'default': None},
7193
+ {'name': 'propvalu', 'type': 'prim', 'desc': 'The value for the property.', 'default': None},
7143
7194
  {'name': 'propcmpr', 'type': 'str', 'desc': 'The comparison operation to use on the value.',
7144
7195
  'default': '='},
7145
7196
  ),
7146
- 'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of buid, sode values.', }}},
7197
+ 'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of node iden, sode values.', }}},
7147
7198
  {'name': 'setStorNodeProp',
7148
7199
  'desc': 'Set a property on a node in this layer.',
7149
7200
  'type': {'type': 'function', '_funcname': 'setStorNodeProp',
@@ -7153,6 +7204,13 @@ class Layer(Prim):
7153
7204
  {'name': 'valu', 'type': 'any', 'desc': 'The value to set.'},
7154
7205
  ),
7155
7206
  'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7207
+ {'name': 'delStorNode',
7208
+ 'desc': 'Delete a storage node, node data, and associated edges from a node in this layer.',
7209
+ 'type': {'type': 'function', '_funcname': 'delStorNode',
7210
+ 'args': (
7211
+ {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7212
+ ),
7213
+ 'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7156
7214
  {'name': 'delStorNodeProp',
7157
7215
  'desc': 'Delete a property from a node in this layer.',
7158
7216
  'type': {'type': 'function', '_funcname': 'delStorNodeProp',
@@ -7161,6 +7219,23 @@ class Layer(Prim):
7161
7219
  {'name': 'prop', 'type': 'str', 'desc': 'The property name to delete.'},
7162
7220
  ),
7163
7221
  'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7222
+ {'name': 'delNodeData',
7223
+ 'desc': 'Delete node data from a node in this layer.',
7224
+ 'type': {'type': 'function', '_funcname': 'delNodeData',
7225
+ 'args': (
7226
+ {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7227
+ {'name': 'name', 'type': 'str', 'default': None, 'desc': 'The node data key to delete.'},
7228
+ ),
7229
+ 'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7230
+ {'name': 'delEdge',
7231
+ 'desc': 'Delete edges from a node in this layer.',
7232
+ 'type': {'type': 'function', '_funcname': 'delEdge',
7233
+ 'args': (
7234
+ {'name': 'nodeid1', 'type': 'str', 'desc': 'The hex string of the N1 node iden.'},
7235
+ {'name': 'verb', 'type': 'str', 'desc': 'The edge verb to delete.'},
7236
+ {'name': 'nodeid2', 'type': 'str', 'desc': 'The hex string of the N2 node iden.'},
7237
+ ),
7238
+ 'returns': {'type': 'boolean', 'desc': 'Returns true if edits were made.'}}},
7164
7239
  {'name': 'getMirrorStatus', 'desc': '''
7165
7240
  Return a dictionary of the mirror synchronization status for the layer.
7166
7241
  ''',
@@ -7215,7 +7290,7 @@ class Layer(Prim):
7215
7290
  'type': {'type': 'function', '_funcname': 'liftByProp',
7216
7291
  'args': (
7217
7292
  {'name': 'propname', 'type': 'str', 'desc': 'The full property name to lift by.'},
7218
- {'name': 'propvalu', 'type': 'obj', 'desc': 'The value for the property.', 'default': None},
7293
+ {'name': 'propvalu', 'type': 'any', 'desc': 'The value for the property.', 'default': None},
7219
7294
  {'name': 'propcmpr', 'type': 'str', 'desc': 'The comparison operation to use on the value.', 'default': '='},
7220
7295
  ),
7221
7296
  'returns': {'name': 'Yields', 'type': 'node',
@@ -7257,6 +7332,16 @@ class Layer(Prim):
7257
7332
  'returns': {'name': 'Yields', 'type': 'node',
7258
7333
  'desc': 'Yields nodes.', }}},
7259
7334
 
7335
+ {'name': 'hasEdge', 'desc': 'Check if a light edge between two nodes exists in the layer.',
7336
+ 'type': {'type': 'function', '_funcname': 'hasEdge',
7337
+ 'args': (
7338
+ {'name': 'nodeid1', 'type': 'str', 'desc': 'The hex string of the N1 node iden.'},
7339
+ {'name': 'verb', 'type': 'str', 'desc': 'The edge verb.'},
7340
+ {'name': 'nodeid2', 'type': 'str', 'desc': 'The hex string of the N2 node iden.'},
7341
+ ),
7342
+ 'returns': {'type': 'boolean',
7343
+ 'desc': 'True if the edge exists in the layer, False if it does not.', }}},
7344
+
7260
7345
  {'name': 'getEdges', 'desc': '''
7261
7346
  Yield (n1iden, verb, n2iden) tuples for any light edges in the layer.
7262
7347
 
@@ -7287,6 +7372,7 @@ class Layer(Prim):
7287
7372
  'type': {'type': 'function', '_funcname': 'getEdgesByN1',
7288
7373
  'args': (
7289
7374
  {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7375
+ {'name': 'verb', 'type': 'str', 'desc': 'An optional edge verb to filter by.', 'default': None},
7290
7376
  ),
7291
7377
  'returns': {'name': 'Yields', 'type': 'list',
7292
7378
  'desc': 'Yields (<verb>, <n2iden>) tuples', }}},
@@ -7304,6 +7390,7 @@ class Layer(Prim):
7304
7390
  'type': {'type': 'function', '_funcname': 'getEdgesByN2',
7305
7391
  'args': (
7306
7392
  {'name': 'nodeid', 'type': 'str', 'desc': 'The hex string of the node iden.'},
7393
+ {'name': 'verb', 'type': 'str', 'desc': 'An optional edge verb to filter by.', 'default': None},
7307
7394
  ),
7308
7395
  'returns': {'name': 'Yields', 'type': 'list',
7309
7396
  'desc': 'Yields (<verb>, <n1iden>) tuples', }}},
@@ -7366,6 +7453,7 @@ class Layer(Prim):
7366
7453
  'delPush': self._delPush,
7367
7454
  'addPull': self._addPull,
7368
7455
  'delPull': self._delPull,
7456
+ 'hasEdge': self.hasEdge,
7369
7457
  'getEdges': self.getEdges,
7370
7458
  'liftByTag': self.liftByTag,
7371
7459
  'liftByProp': self.liftByProp,
@@ -7385,7 +7473,10 @@ class Layer(Prim):
7385
7473
  'getNodeData': self.getNodeData,
7386
7474
  'getMirrorStatus': self.getMirrorStatus,
7387
7475
  'setStorNodeProp': self.setStorNodeProp,
7476
+ 'delStorNode': self.delStorNode,
7388
7477
  'delStorNodeProp': self.delStorNodeProp,
7478
+ 'delNodeData': self.delNodeData,
7479
+ 'delEdge': self.delEdge,
7389
7480
  }
7390
7481
 
7391
7482
  @stormfunc(readonly=True)
@@ -7403,8 +7494,7 @@ class Layer(Prim):
7403
7494
  async for _, buid, sode in layr.liftByTag(tagname, form=formname):
7404
7495
  yield await self.runt.snap._joinStorNode(buid, {iden: sode})
7405
7496
 
7406
- @stormfunc(readonly=True)
7407
- async def liftByProp(self, propname, propvalu=None, propcmpr='='):
7497
+ async def _liftByProp(self, propname, propvalu=None, propcmpr='='):
7408
7498
 
7409
7499
  propname = await tostr(propname)
7410
7500
  propvalu = await toprim(propvalu)
@@ -7432,12 +7522,18 @@ class Layer(Prim):
7432
7522
 
7433
7523
  if propvalu is None:
7434
7524
  async for _, buid, sode in layr.liftByProp(liftform, liftprop):
7435
- yield await self.runt.snap._joinStorNode(buid, {iden: sode})
7525
+ yield buid, sode
7436
7526
  return
7437
7527
 
7438
7528
  norm, info = prop.type.norm(propvalu)
7439
7529
  cmprvals = prop.type.getStorCmprs(propcmpr, norm)
7440
7530
  async for _, buid, sode in layr.liftByPropValu(liftform, liftprop, cmprvals):
7531
+ yield buid, sode
7532
+
7533
+ @stormfunc(readonly=True)
7534
+ async def liftByProp(self, propname, propvalu=None, propcmpr='='):
7535
+ iden = self.valu.get('iden')
7536
+ async for buid, sode in self._liftByProp(propname, propvalu=propvalu, propcmpr=propcmpr):
7441
7537
  yield await self.runt.snap._joinStorNode(buid, {iden: sode})
7442
7538
 
7443
7539
  @stormfunc(readonly=True)
@@ -7460,24 +7556,56 @@ class Layer(Prim):
7460
7556
  return await layr.getMirrorStatus()
7461
7557
 
7462
7558
  async def setStorNodeProp(self, nodeid, prop, valu):
7463
- iden = self.valu.get('iden')
7464
- layr = self.runt.snap.core.getLayer(iden)
7465
- buid = s_common.uhex(await tostr(nodeid))
7559
+ buid = await tobuid(nodeid)
7466
7560
  prop = await tostr(prop)
7467
7561
  valu = await tostor(valu)
7562
+
7563
+ iden = self.valu.get('iden')
7564
+ layr = self.runt.snap.core.getLayer(iden)
7468
7565
  self.runt.reqAdmin(mesg='setStorNodeProp() requires admin privileges.')
7469
7566
  meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7470
7567
  return await layr.setStorNodeProp(buid, prop, valu, meta=meta)
7471
7568
 
7472
- async def delStorNodeProp(self, nodeid, prop):
7569
+ async def delStorNode(self, nodeid):
7570
+ buid = await tobuid(nodeid)
7571
+
7473
7572
  iden = self.valu.get('iden')
7474
7573
  layr = self.runt.snap.core.getLayer(iden)
7475
- buid = s_common.uhex(await tostr(nodeid))
7574
+ self.runt.reqAdmin(mesg='delStorNode() requires admin privileges.')
7575
+ meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7576
+ return await layr.delStorNode(buid, meta=meta)
7577
+
7578
+ async def delStorNodeProp(self, nodeid, prop):
7579
+ buid = await tobuid(nodeid)
7476
7580
  prop = await tostr(prop)
7581
+
7582
+ iden = self.valu.get('iden')
7583
+ layr = self.runt.snap.core.getLayer(iden)
7477
7584
  self.runt.reqAdmin(mesg='delStorNodeProp() requires admin privileges.')
7478
7585
  meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7479
7586
  return await layr.delStorNodeProp(buid, prop, meta=meta)
7480
7587
 
7588
+ async def delNodeData(self, nodeid, name=None):
7589
+ buid = await tobuid(nodeid)
7590
+ name = await tostr(name, noneok=True)
7591
+
7592
+ iden = self.valu.get('iden')
7593
+ layr = self.runt.snap.core.getLayer(iden)
7594
+ self.runt.reqAdmin(mesg='delNodeData() requires admin privileges.')
7595
+ meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7596
+ return await layr.delNodeData(buid, meta=meta, name=name)
7597
+
7598
+ async def delEdge(self, nodeid1, verb, nodeid2):
7599
+ n1buid = await tobuid(nodeid1)
7600
+ verb = await tostr(verb)
7601
+ n2buid = await tobuid(nodeid2)
7602
+
7603
+ iden = self.valu.get('iden')
7604
+ layr = self.runt.snap.core.getLayer(iden)
7605
+ self.runt.reqAdmin(mesg='delEdge() requires admin privileges.')
7606
+ meta = {'time': s_common.now(), 'user': self.runt.user.iden}
7607
+ return await layr.delEdge(n1buid, verb, n2buid, meta=meta)
7608
+
7481
7609
  async def _addPull(self, url, offs=0, queue_size=s_const.layer_pdef_qsize, chunk_size=s_const.layer_pdef_csize):
7482
7610
  url = await tostr(url)
7483
7611
  offs = await toint(offs)
@@ -7733,11 +7861,11 @@ class Layer(Prim):
7733
7861
 
7734
7862
  @stormfunc(readonly=True)
7735
7863
  async def getStorNode(self, nodeid):
7736
- nodeid = await tostr(nodeid)
7864
+ nodeid = await tobuid(nodeid)
7737
7865
  layriden = self.valu.get('iden')
7738
7866
  await self.runt.reqUserCanReadLayer(layriden)
7739
7867
  layr = self.runt.snap.core.getLayer(layriden)
7740
- return await layr.getStorNode(s_common.uhex(nodeid))
7868
+ return await layr.getStorNode(nodeid)
7741
7869
 
7742
7870
  @stormfunc(readonly=True)
7743
7871
  async def getStorNodes(self):
@@ -7762,25 +7890,18 @@ class Layer(Prim):
7762
7890
 
7763
7891
  @stormfunc(readonly=True)
7764
7892
  async def getStorNodesByProp(self, propname, propvalu=None, propcmpr='='):
7765
- propname = await tostr(propname)
7766
- propvalu = await tostor(propvalu)
7767
- propcmpr = await tostr(propcmpr)
7893
+ async for buid, sode in self._liftByProp(propname, propvalu=propvalu, propcmpr=propcmpr):
7894
+ yield s_common.ehex(buid), sode
7768
7895
 
7896
+ @stormfunc(readonly=True)
7897
+ async def hasEdge(self, nodeid1, verb, nodeid2):
7898
+ nodeid1 = await tobuid(nodeid1)
7899
+ verb = await tostr(verb)
7900
+ nodeid2 = await tobuid(nodeid2)
7769
7901
  layriden = self.valu.get('iden')
7770
7902
  await self.runt.reqUserCanReadLayer(layriden)
7771
7903
  layr = self.runt.snap.core.getLayer(layriden)
7772
-
7773
- prop = self.runt.snap.core.model.reqProp(propname)
7774
-
7775
- if propvalu is not None:
7776
- norm, info = prop.type.norm(propvalu)
7777
- cmprvals = prop.type.getStorCmprs(propcmpr, norm)
7778
- async for _, buid, sode in layr.liftByPropValu(prop.form.name, prop.name, cmprvals):
7779
- yield (s_common.ehex(buid), sode)
7780
- return
7781
-
7782
- async for _, buid, sode in layr.liftByProp(prop.form.name, prop.name):
7783
- yield (s_common.ehex(buid), sode)
7904
+ return await layr.hasNodeEdge(nodeid1, verb, nodeid2)
7784
7905
 
7785
7906
  @stormfunc(readonly=True)
7786
7907
  async def getEdges(self):
@@ -7791,30 +7912,32 @@ class Layer(Prim):
7791
7912
  yield item
7792
7913
 
7793
7914
  @stormfunc(readonly=True)
7794
- async def getEdgesByN1(self, nodeid):
7795
- nodeid = await tostr(nodeid)
7915
+ async def getEdgesByN1(self, nodeid, verb=None):
7916
+ nodeid = await tobuid(nodeid)
7917
+ verb = await tostr(verb, noneok=True)
7796
7918
  layriden = self.valu.get('iden')
7797
7919
  await self.runt.reqUserCanReadLayer(layriden)
7798
7920
  layr = self.runt.snap.core.getLayer(layriden)
7799
- async for item in layr.iterNodeEdgesN1(s_common.uhex(nodeid)):
7921
+ async for item in layr.iterNodeEdgesN1(nodeid, verb=verb):
7800
7922
  yield item
7801
7923
 
7802
7924
  @stormfunc(readonly=True)
7803
- async def getEdgesByN2(self, nodeid):
7804
- nodeid = await tostr(nodeid)
7925
+ async def getEdgesByN2(self, nodeid, verb=None):
7926
+ nodeid = await tobuid(nodeid)
7927
+ verb = await tostr(verb, noneok=True)
7805
7928
  layriden = self.valu.get('iden')
7806
7929
  await self.runt.reqUserCanReadLayer(layriden)
7807
7930
  layr = self.runt.snap.core.getLayer(layriden)
7808
- async for item in layr.iterNodeEdgesN2(s_common.uhex(nodeid)):
7931
+ async for item in layr.iterNodeEdgesN2(nodeid, verb=verb):
7809
7932
  yield item
7810
7933
 
7811
7934
  @stormfunc(readonly=True)
7812
7935
  async def getNodeData(self, nodeid):
7813
- nodeid = await tostr(nodeid)
7936
+ nodeid = await tobuid(nodeid)
7814
7937
  layriden = self.valu.get('iden')
7815
7938
  await self.runt.reqUserCanReadLayer(layriden)
7816
7939
  layr = self.runt.snap.core.getLayer(layriden)
7817
- async for item in layr.iterNodeData(s_common.uhex(nodeid)):
7940
+ async for item in layr.iterNodeData(nodeid):
7818
7941
  yield item
7819
7942
 
7820
7943
  @stormfunc(readonly=True)
@@ -9115,39 +9238,39 @@ class LibJsonStor(Lib):
9115
9238
  {'name': 'get', 'desc': 'Return a stored JSON object or object property.',
9116
9239
  'type': {'type': 'function', '_funcname': 'get',
9117
9240
  'args': (
9118
- {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path parts.'},
9119
- {'name': 'prop', 'type': 'str|list', 'desc': 'A property name or list of name parts.', 'default': None},
9241
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'A path string or list of path parts.'},
9242
+ {'name': 'prop', 'type': ['str', 'list'], 'desc': 'A property name or list of name parts.', 'default': None},
9120
9243
  ),
9121
9244
  'returns': {'type': 'prim', 'desc': 'The previously stored value or ``(null)``.'}}},
9122
9245
 
9123
9246
  {'name': 'set', 'desc': 'Set a JSON object or object property.',
9124
9247
  'type': {'type': 'function', '_funcname': 'set',
9125
9248
  'args': (
9126
- {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path elements.'},
9249
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'A path string or list of path elements.'},
9127
9250
  {'name': 'valu', 'type': 'prim', 'desc': 'The value to set as the JSON object or object property.'},
9128
- {'name': 'prop', 'type': 'str|list', 'desc': 'A property name or list of name parts.', 'default': None},
9251
+ {'name': 'prop', 'type': ['str', 'list'], 'desc': 'A property name or list of name parts.', 'default': None},
9129
9252
  ),
9130
9253
  'returns': {'type': 'boolean', 'desc': 'True if the set operation was successful.'}}},
9131
9254
 
9132
9255
  {'name': 'del', 'desc': 'Delete a stored JSON object or object.',
9133
9256
  'type': {'type': 'function', '_funcname': '_del',
9134
9257
  'args': (
9135
- {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path parts.'},
9136
- {'name': 'prop', 'type': 'str|list', 'desc': 'A property name or list of name parts.', 'default': None},
9258
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'A path string or list of path parts.'},
9259
+ {'name': 'prop', 'type': ['str', 'list'], 'desc': 'A property name or list of name parts.', 'default': None},
9137
9260
  ),
9138
9261
  'returns': {'type': 'boolean', 'desc': 'True if the del operation was successful.'}}},
9139
9262
 
9140
9263
  {'name': 'iter', 'desc': 'Yield (<path>, <valu>) tuples for the JSON objects.',
9141
9264
  'type': {'type': 'function', '_funcname': 'iter',
9142
9265
  'args': (
9143
- {'name': 'path', 'type': 'str|list', 'desc': 'A path string or list of path parts.', 'default': None},
9266
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'A path string or list of path parts.', 'default': None},
9144
9267
  ),
9145
9268
  'returns': {'name': 'Yields', 'type': 'list', 'desc': '(<path>, <item>) tuples.'}}},
9146
9269
  {'name': 'cacheget',
9147
9270
  'desc': 'Retrieve data stored with cacheset() if it was stored more recently than the asof argument.',
9148
9271
  'type': {'type': 'function', '_funcname': 'cacheget',
9149
9272
  'args': (
9150
- {'name': 'path', 'type': 'str|list', 'desc': 'The base path to use for the cache key.', },
9273
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'The base path to use for the cache key.', },
9151
9274
  {'name': 'key', 'type': 'prim', 'desc': 'The value to use for the GUID cache key.', },
9152
9275
  {'name': 'asof', 'type': 'time', 'default': 'now', 'desc': 'The max cache age.'},
9153
9276
  {'name': 'envl', 'type': 'boolean', 'default': False, 'desc': 'Return the full cache envelope.'},
@@ -9157,7 +9280,7 @@ class LibJsonStor(Lib):
9157
9280
  'desc': 'Set cache data with an envelope that tracks time for cacheget() use.',
9158
9281
  'type': {'type': 'function', '_funcname': 'cacheset',
9159
9282
  'args': (
9160
- {'name': 'path', 'type': 'str|list', 'desc': 'The base path to use for the cache key.', },
9283
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'The base path to use for the cache key.', },
9161
9284
  {'name': 'key', 'type': 'prim', 'desc': 'The value to use for the GUID cache key.', },
9162
9285
  {'name': 'valu', 'type': 'prim', 'desc': 'The data to store.', },
9163
9286
  ),
@@ -9166,7 +9289,7 @@ class LibJsonStor(Lib):
9166
9289
  'desc': 'Remove cached data set with cacheset.',
9167
9290
  'type': {'type': 'function', '_funcname': 'cachedel',
9168
9291
  'args': (
9169
- {'name': 'path', 'type': 'str|list', 'desc': 'The base path to use for the cache key.', },
9292
+ {'name': 'path', 'type': ['str', 'list'], 'desc': 'The base path to use for the cache key.', },
9170
9293
  {'name': 'key', 'type': 'prim', 'desc': 'The value to use for the GUID cache key.', },
9171
9294
  ),
9172
9295
  'returns': {'type': 'boolean', 'desc': 'True if the del operation was successful.'}}},
@@ -9395,7 +9518,7 @@ class LibCron(Lib):
9395
9518
  {'name': 'prefix', 'type': 'str',
9396
9519
  'desc': 'A prefix to match in order to identify a cron job to modify. '
9397
9520
  'Only a single matching prefix will be modified.', },
9398
- {'name': 'query', 'type': ['str', 'query'],
9521
+ {'name': 'query', 'type': ['str', 'storm:query'],
9399
9522
  'desc': 'The new Storm query for the Cron Job.', }
9400
9523
  ),
9401
9524
  'returns': {'type': 'str', 'desc': 'The iden of the CronJob which was modified.'}}},
@@ -10235,24 +10358,44 @@ async def torepr(valu, usestr=False):
10235
10358
  return str(valu)
10236
10359
  return repr(valu)
10237
10360
 
10238
- async def tobuidhex(valu, noneok=False):
10239
-
10240
- if noneok and valu is None:
10241
- return None
10361
+ async def tobuid(valu):
10242
10362
 
10243
10363
  if isinstance(valu, Node):
10244
- return valu.valu.iden()
10364
+ return valu.valu.buid
10245
10365
 
10246
10366
  if isinstance(valu, s_node.Node):
10247
- return valu.iden()
10367
+ return valu.buid
10368
+
10369
+ valu = await toprim(valu)
10370
+
10371
+ if isinstance(valu, str):
10372
+ if not s_common.isbuidhex(valu):
10373
+ mesg = f'Invalid buid string: {valu}'
10374
+ raise s_exc.BadCast(mesg=mesg)
10248
10375
 
10249
- valu = await tostr(valu)
10250
- if not s_common.isbuidhex(valu):
10251
- mesg = f'Invalid buid string: {valu}'
10376
+ return s_common.uhex(valu)
10377
+
10378
+ if not isinstance(valu, bytes):
10379
+ mesg = f'Invalid buid valu: {valu}'
10380
+ raise s_exc.BadCast(mesg=mesg)
10381
+
10382
+ if len(valu) != 32:
10383
+ mesg = f'Invalid buid valu: {valu}'
10252
10384
  raise s_exc.BadCast(mesg=mesg)
10253
10385
 
10254
10386
  return valu
10255
10387
 
10388
+ async def tobuidhex(valu, noneok=False):
10389
+
10390
+ if noneok and valu is None:
10391
+ return None
10392
+
10393
+ if isinstance(valu, str) and s_common.isbuidhex(valu):
10394
+ return valu
10395
+
10396
+ buid = await tobuid(valu)
10397
+ return s_common.ehex(buid)
10398
+
10256
10399
  async def totype(valu, basetypes=False) -> str:
10257
10400
  '''
10258
10401
  Convert a value to its Storm type string.
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, 219, 0)
226
+ version = (2, 221, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '9e442b257a7ddfa8d476eafdcad21c3a0440cc83'
228
+ commit = '6ae7cd60524df7dc861bb91fe25309999eba6d70'
@@ -1,9 +1,9 @@
1
1
  import lark # type: ignore
2
2
 
3
3
  import synapse.exc as s_exc
4
+ import synapse.data as s_data
4
5
 
5
6
  import synapse.lib.parser as s_parser
6
- import synapse.lib.datfile as s_datfile
7
7
  import synapse.lib.grammar as s_grammar
8
8
 
9
9
  import synapse.tests.utils as s_t_utils
@@ -1414,9 +1414,7 @@ class GrammarTest(s_t_utils.SynTest):
1414
1414
  '''
1415
1415
  Validates that we have no grammar ambiguities
1416
1416
  '''
1417
- with s_datfile.openDatFile('synapse.lib/storm.lark') as larkf:
1418
- grammar = larkf.read().decode()
1419
-
1417
+ grammar = s_data.getLark('storm')
1420
1418
  parser = lark.Lark(grammar, start='query', debug=True, regex=True, parser='lalr',
1421
1419
  keep_all_tokens=True, maybe_placeholders=False,
1422
1420
  propagate_positions=True)
@@ -125,6 +125,35 @@ class JsonTest(s_test.SynTest):
125
125
 
126
126
  self.eq(valu, s_json.loads(s_json.dumps(valu)))
127
127
 
128
+ async def test_lib_json_control_strings(self):
129
+ valus = [
130
+ 'line1"line2',
131
+ 'line1/line2',
132
+ 'line1\\line2',
133
+ 'line1\bline2',
134
+ 'line1\fline2',
135
+ 'line1\nline2',
136
+ 'line1\rline2',
137
+ 'line1\tline2',
138
+ 'line1\u0009line2',
139
+ 'line1\u1000line2',
140
+ 'line1\u2000line2',
141
+ 'line1\u3000line2',
142
+ ]
143
+
144
+ with self.getLoggerStream('synapse.lib.json') as stream:
145
+ async with self.getTestCore() as core:
146
+ for valu in valus:
147
+ q = '$lib.print($valu) $lib.print($lib.json.save($valu))'
148
+ msgs = await core.stormlist(q, opts={'vars': {'valu': valu}})
149
+ self.stormHasNoWarnErr(msgs)
150
+
151
+ self.eq(s_json.loads(s_json.dumps(valu)), valu)
152
+
153
+ stream.seek(0)
154
+ data = stream.read()
155
+ self.notin('fallback JSON', data)
156
+
128
157
  async def test_jsload(self):
129
158
  with self.getTestDir() as dirn:
130
159
  with s_common.genfile(dirn, 'jsload.json') as fp: