synapse 2.191.0__py311-none-any.whl → 2.193.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 (46) hide show
  1. synapse/axon.py +54 -23
  2. synapse/common.py +15 -0
  3. synapse/cortex.py +18 -20
  4. synapse/exc.py +6 -1
  5. synapse/lib/agenda.py +0 -2
  6. synapse/lib/ast.py +30 -12
  7. synapse/lib/cell.py +79 -85
  8. synapse/lib/cli.py +20 -11
  9. synapse/lib/nexus.py +2 -1
  10. synapse/lib/parser.py +1 -1
  11. synapse/lib/snap.py +4 -4
  12. synapse/lib/storm.py +34 -17
  13. synapse/lib/stormhttp.py +32 -35
  14. synapse/lib/stormlib/json.py +5 -2
  15. synapse/lib/stormtypes.py +121 -20
  16. synapse/lib/version.py +2 -2
  17. synapse/models/inet.py +17 -1
  18. synapse/models/infotech.py +14 -4
  19. synapse/models/risk.py +16 -2
  20. synapse/tests/test_axon.py +10 -0
  21. synapse/tests/test_cortex.py +55 -3
  22. synapse/tests/test_exc.py +3 -0
  23. synapse/tests/test_lib_agenda.py +157 -1
  24. synapse/tests/test_lib_ast.py +49 -1
  25. synapse/tests/test_lib_cell.py +106 -1
  26. synapse/tests/test_lib_httpapi.py +9 -2
  27. synapse/tests/test_lib_storm.py +72 -30
  28. synapse/tests/test_lib_stormhttp.py +57 -12
  29. synapse/tests/test_lib_stormlib_json.py +20 -0
  30. synapse/tests/test_lib_stormlib_scrape.py +2 -2
  31. synapse/tests/test_model_inet.py +40 -5
  32. synapse/tests/test_model_risk.py +2 -0
  33. synapse/tests/test_servers_univ.py +0 -12
  34. synapse/tests/test_tools_apikey.py +227 -0
  35. synapse/tests/test_tools_storm.py +95 -0
  36. synapse/tests/test_utils_getrefs.py +1 -1
  37. synapse/tools/apikey.py +93 -0
  38. synapse/utils/getrefs.py +14 -3
  39. synapse/vendor/cpython/lib/http/__init__.py +0 -0
  40. synapse/vendor/cpython/lib/http/cookies.py +59 -0
  41. synapse/vendor/cpython/lib/test/test_http_cookies.py +49 -0
  42. {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/METADATA +2 -2
  43. {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/RECORD +46 -41
  44. {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/WHEEL +1 -1
  45. {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/LICENSE +0 -0
  46. {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/top_level.txt +0 -0
synapse/axon.py CHANGED
@@ -594,7 +594,7 @@ class AxonApi(s_cell.CellApi, s_share.Share): # type: ignore
594
594
  return await self.cell.dels(sha256s)
595
595
 
596
596
  async def wget(self, url, params=None, headers=None, json=None, body=None, method='GET',
597
- ssl=True, timeout=None, proxy=None, ssl_opts=None):
597
+ ssl=True, timeout=None, proxy=True, ssl_opts=None):
598
598
  '''
599
599
  Stream a file download directly into the Axon.
600
600
 
@@ -607,6 +607,7 @@ class AxonApi(s_cell.CellApi, s_share.Share): # type: ignore
607
607
  method (str): The HTTP method to use.
608
608
  ssl (bool): Perform SSL verification.
609
609
  timeout (int): The timeout of the request, in seconds.
610
+ proxy (str|bool): The proxy value.
610
611
  ssl_opts (dict): Additional SSL/TLS options.
611
612
 
612
613
  Notes:
@@ -621,6 +622,13 @@ class AxonApi(s_cell.CellApi, s_share.Share): # type: ignore
621
622
  'client_key': <str> - PEM encoded key for use in mTLS. Alternatively, can be included in client_cert.
622
623
  }
623
624
 
625
+ The following proxy arguments are supported::
626
+
627
+ None: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
628
+ True: Use the proxy defined by the http:proxy configuration option if set.
629
+ False: Do not use the proxy defined by the http:proxy configuration option if set.
630
+ <str>: A proxy URL string.
631
+
624
632
  The dictionary returned by this may contain the following values::
625
633
 
626
634
  {
@@ -654,13 +662,13 @@ class AxonApi(s_cell.CellApi, s_share.Share): # type: ignore
654
662
  ssl=ssl, timeout=timeout, proxy=proxy, ssl_opts=ssl_opts)
655
663
 
656
664
  async def postfiles(self, fields, url, params=None, headers=None, method='POST',
657
- ssl=True, timeout=None, proxy=None, ssl_opts=None):
665
+ ssl=True, timeout=None, proxy=True, ssl_opts=None):
658
666
  await self._reqUserAllowed(('axon', 'wput'))
659
667
  return await self.cell.postfiles(fields, url, params=params, headers=headers, method=method,
660
668
  ssl=ssl, timeout=timeout, proxy=proxy, ssl_opts=ssl_opts)
661
669
 
662
670
  async def wput(self, sha256, url, params=None, headers=None, method='PUT',
663
- ssl=True, timeout=None, proxy=None, ssl_opts=None):
671
+ ssl=True, timeout=None, proxy=True, ssl_opts=None):
664
672
  await self._reqUserAllowed(('axon', 'wput'))
665
673
  return await self.cell.wput(sha256, url, params=params, headers=headers, method=method,
666
674
  ssl=ssl, timeout=timeout, proxy=proxy, ssl_opts=ssl_opts)
@@ -935,6 +943,24 @@ class Axon(s_cell.Cell):
935
943
  self.axonhist.add(item, tick=tick)
936
944
  self.axonseqn.add(item)
937
945
 
946
+ async def _resolveProxyUrl(self, valu):
947
+ match valu:
948
+ case None:
949
+ s_common.deprecated('Setting the Axon HTTP proxy argument to None', curv='2.192.0')
950
+ return await self.getConfOpt('http:proxy')
951
+
952
+ case True:
953
+ return await self.getConfOpt('http:proxy')
954
+
955
+ case False:
956
+ return None
957
+
958
+ case str():
959
+ return valu
960
+
961
+ case _:
962
+ raise s_exc.BadArg(mesg='HTTP proxy argument must be a string or bool.')
963
+
938
964
  async def _reqHas(self, sha256):
939
965
  '''
940
966
  Ensure a file exists; and return its size if so.
@@ -1462,7 +1488,7 @@ class Axon(s_cell.Cell):
1462
1488
  sha256=sha256) from None
1463
1489
 
1464
1490
  async def postfiles(self, fields, url, params=None, headers=None, method='POST',
1465
- ssl=True, timeout=None, proxy=None, ssl_opts=None):
1491
+ ssl=True, timeout=None, proxy=True, ssl_opts=None):
1466
1492
  '''
1467
1493
  Send files from the axon as fields in a multipart/form-data HTTP request.
1468
1494
 
@@ -1474,7 +1500,7 @@ class Axon(s_cell.Cell):
1474
1500
  method (str): The HTTP method to use.
1475
1501
  ssl (bool): Perform SSL verification.
1476
1502
  timeout (int): The timeout of the request, in seconds.
1477
- proxy (bool|str|null): Use a specific proxy or disable proxy use.
1503
+ proxy (str|bool): The proxy value.
1478
1504
  ssl_opts (dict): Additional SSL/TLS options.
1479
1505
 
1480
1506
  Notes:
@@ -1497,6 +1523,13 @@ class Axon(s_cell.Cell):
1497
1523
  'client_key': <str> - PEM encoded key for use in mTLS. Alternatively, can be included in client_cert.
1498
1524
  }
1499
1525
 
1526
+ The following proxy arguments are supported::
1527
+
1528
+ None: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
1529
+ True: Use the proxy defined by the http:proxy configuration option if set.
1530
+ False: Do not use the proxy defined by the http:proxy configuration option if set.
1531
+ <str>: A proxy URL string.
1532
+
1500
1533
  The dictionary returned by this may contain the following values::
1501
1534
 
1502
1535
  {
@@ -1512,14 +1545,11 @@ class Axon(s_cell.Cell):
1512
1545
  Returns:
1513
1546
  dict: An information dictionary containing the results of the request.
1514
1547
  '''
1515
- if proxy is None:
1516
- proxy = self.conf.get('http:proxy')
1517
-
1518
1548
  ssl = self.getCachedSslCtx(opts=ssl_opts, verify=ssl)
1519
1549
 
1520
1550
  connector = None
1521
- if proxy:
1522
- connector = aiohttp_socks.ProxyConnector.from_url(proxy)
1551
+ if proxyurl := await self._resolveProxyUrl(proxy):
1552
+ connector = aiohttp_socks.ProxyConnector.from_url(proxyurl)
1523
1553
 
1524
1554
  atimeout = aiohttp.ClientTimeout(total=timeout)
1525
1555
 
@@ -1583,18 +1613,15 @@ class Axon(s_cell.Cell):
1583
1613
  }
1584
1614
 
1585
1615
  async def wput(self, sha256, url, params=None, headers=None, method='PUT', ssl=True, timeout=None,
1586
- filename=None, filemime=None, proxy=None, ssl_opts=None):
1616
+ filename=None, filemime=None, proxy=True, ssl_opts=None):
1587
1617
  '''
1588
1618
  Stream a blob from the axon as the body of an HTTP request.
1589
1619
  '''
1590
- if proxy is None:
1591
- proxy = self.conf.get('http:proxy')
1592
-
1593
1620
  ssl = self.getCachedSslCtx(opts=ssl_opts, verify=ssl)
1594
1621
 
1595
1622
  connector = None
1596
- if proxy:
1597
- connector = aiohttp_socks.ProxyConnector.from_url(proxy)
1623
+ if proxyurl := await self._resolveProxyUrl(proxy):
1624
+ connector = aiohttp_socks.ProxyConnector.from_url(proxyurl)
1598
1625
 
1599
1626
  atimeout = aiohttp.ClientTimeout(total=timeout)
1600
1627
 
@@ -1654,7 +1681,7 @@ class Axon(s_cell.Cell):
1654
1681
  return info
1655
1682
 
1656
1683
  async def wget(self, url, params=None, headers=None, json=None, body=None, method='GET',
1657
- ssl=True, timeout=None, proxy=None, ssl_opts=None):
1684
+ ssl=True, timeout=None, proxy=True, ssl_opts=None):
1658
1685
  '''
1659
1686
  Stream a file download directly into the Axon.
1660
1687
 
@@ -1667,7 +1694,7 @@ class Axon(s_cell.Cell):
1667
1694
  method (str): The HTTP method to use.
1668
1695
  ssl (bool): Perform SSL verification.
1669
1696
  timeout (int): The timeout of the request, in seconds.
1670
- proxy (bool|str|null): Use a specific proxy or disable proxy use.
1697
+ proxy (str|bool): The proxy value.
1671
1698
  ssl_opts (dict): Additional SSL/TLS options.
1672
1699
 
1673
1700
  Notes:
@@ -1682,6 +1709,13 @@ class Axon(s_cell.Cell):
1682
1709
  'client_key': <str> - PEM encoded key for use in mTLS. Alternatively, can be included in client_cert.
1683
1710
  }
1684
1711
 
1712
+ The following proxy arguments are supported::
1713
+
1714
+ None: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
1715
+ True: Use the proxy defined by the http:proxy configuration option if set.
1716
+ False: Do not use the proxy defined by the http:proxy configuration option if set.
1717
+ <str>: A proxy URL string.
1718
+
1685
1719
  The dictionary returned by this may contain the following values::
1686
1720
 
1687
1721
  {
@@ -1712,14 +1746,11 @@ class Axon(s_cell.Cell):
1712
1746
  '''
1713
1747
  logger.debug(f'Wget called for [{url}].', extra=await self.getLogExtra(url=s_urlhelp.sanitizeUrl(url)))
1714
1748
 
1715
- if proxy is None:
1716
- proxy = self.conf.get('http:proxy')
1717
-
1718
1749
  ssl = self.getCachedSslCtx(opts=ssl_opts, verify=ssl)
1719
1750
 
1720
1751
  connector = None
1721
- if proxy:
1722
- connector = aiohttp_socks.ProxyConnector.from_url(proxy)
1752
+ if proxyurl := await self._resolveProxyUrl(proxy):
1753
+ connector = aiohttp_socks.ProxyConnector.from_url(proxyurl)
1723
1754
 
1724
1755
  atimeout = aiohttp.ClientTimeout(total=timeout)
1725
1756
 
synapse/common.py CHANGED
@@ -29,6 +29,8 @@ import traceback
29
29
  import contextlib
30
30
  import collections
31
31
 
32
+ import http.cookies
33
+
32
34
  import yaml
33
35
  import regex
34
36
 
@@ -38,6 +40,8 @@ import synapse.lib.msgpack as s_msgpack
38
40
  import synapse.lib.structlog as s_structlog
39
41
 
40
42
  import synapse.vendor.cpython.lib.ipaddress as ipaddress
43
+ import synapse.vendor.cpython.lib.http.cookies as v_cookies
44
+
41
45
 
42
46
  try:
43
47
  from yaml import CSafeLoader as Loader
@@ -1218,6 +1222,17 @@ def trimText(text: str, n: int = 256, placeholder: str = '...') -> str:
1218
1222
  assert n > plen
1219
1223
  return f'{text[:mlen]}{placeholder}'
1220
1224
 
1225
+ def _patch_http_cookies():
1226
+ '''
1227
+ Patch stdlib http.cookies._unquote from the 3.11.10 implementation if
1228
+ the interpreter we are using is not patched for CVE-2024-7592.
1229
+ '''
1230
+ if not hasattr(http.cookies, '_QuotePatt'):
1231
+ return
1232
+ http.cookies._unquote = v_cookies._unquote
1233
+
1234
+ _patch_http_cookies()
1235
+
1221
1236
  # TODO: Switch back to using asyncio.wait_for when we are using py 3.12+
1222
1237
  # This is a workaround for a race where asyncio.wait_for can end up
1223
1238
  # ignoring cancellation https://github.com/python/cpython/issues/86296
synapse/cortex.py CHANGED
@@ -1739,7 +1739,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
1739
1739
  for filt in gdef.get('filters', ()):
1740
1740
  await self.getStormQuery(filt)
1741
1741
 
1742
- for pivo in gdef.get('filters', ()):
1742
+ for pivo in gdef.get('pivots', ()):
1743
1743
  await self.getStormQuery(pivo)
1744
1744
 
1745
1745
  for form, rule in gdef.get('forms', {}).items():
@@ -1749,7 +1749,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
1749
1749
  for filt in rule.get('filters', ()):
1750
1750
  await self.getStormQuery(filt)
1751
1751
 
1752
- for pivo in rule.get('filters', ()):
1752
+ for pivo in rule.get('pivots', ()):
1753
1753
  await self.getStormQuery(pivo)
1754
1754
 
1755
1755
  async def addStormGraph(self, gdef, user=None):
@@ -2240,7 +2240,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2240
2240
 
2241
2241
  '''
2242
2242
  name = cdef.get('name')
2243
- await self._setStormCmd(cdef)
2243
+ self._setStormCmd(cdef)
2244
2244
  self.cmddefs.set(name, cdef)
2245
2245
 
2246
2246
  async def _reqStormCmd(self, cdef):
@@ -2483,7 +2483,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2483
2483
  async for sodes in self._mergeSodes(layers, genrs, cmprkey_indx, filtercmpr, reverse=reverse):
2484
2484
  yield sodes
2485
2485
 
2486
- async def _setStormCmd(self, cdef):
2486
+ def _setStormCmd(self, cdef):
2487
2487
  '''
2488
2488
  Note:
2489
2489
  No change control or persistence
@@ -2543,13 +2543,9 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2543
2543
  name = cdef.get('name')
2544
2544
  self.stormcmds[name] = ctor
2545
2545
 
2546
- await self.fire('core:cmd:change', cmd=name, act='add')
2547
-
2548
- async def _popStormCmd(self, name):
2546
+ def _popStormCmd(self, name):
2549
2547
  self.stormcmds.pop(name, None)
2550
2548
 
2551
- await self.fire('core:cmd:change', cmd=name, act='del')
2552
-
2553
2549
  async def delStormCmd(self, name):
2554
2550
  '''
2555
2551
  Remove a previously set pure storm command.
@@ -2575,8 +2571,6 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2575
2571
  self.cmddefs.pop(name)
2576
2572
  self.stormcmds.pop(name, None)
2577
2573
 
2578
- await self.fire('core:cmd:change', cmd=name, act='del')
2579
-
2580
2574
  async def addStormPkg(self, pkgdef, verify=False):
2581
2575
  '''
2582
2576
  Add the given storm package to the cortex.
@@ -2630,11 +2624,11 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2630
2624
  olddef = self.pkgdefs.get(name, None)
2631
2625
  if olddef is not None:
2632
2626
  if s_hashitem.hashitem(pkgdef) != s_hashitem.hashitem(olddef):
2633
- await self._dropStormPkg(olddef)
2627
+ self._dropStormPkg(olddef)
2634
2628
  else:
2635
2629
  return
2636
2630
 
2637
- await self.loadStormPkg(pkgdef)
2631
+ self.loadStormPkg(pkgdef)
2638
2632
  self.pkgdefs.set(name, pkgdef)
2639
2633
 
2640
2634
  self._clearPermDefs()
@@ -2664,7 +2658,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2664
2658
  if pkgdef is None:
2665
2659
  return
2666
2660
 
2667
- await self._dropStormPkg(pkgdef)
2661
+ self._dropStormPkg(pkgdef)
2668
2662
 
2669
2663
  self._clearPermDefs()
2670
2664
 
@@ -2713,7 +2707,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2713
2707
  async def _tryLoadStormPkg(self, pkgdef):
2714
2708
  try:
2715
2709
  await self._normStormPkg(pkgdef, validstorm=False)
2716
- await self.loadStormPkg(pkgdef)
2710
+ self.loadStormPkg(pkgdef)
2717
2711
 
2718
2712
  except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
2719
2713
  raise
@@ -2881,7 +2875,9 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2881
2875
  for configvar in pkgdef.get('configvars', ()):
2882
2876
  self._reqStormPkgVarType(pkgname, configvar.get('type'))
2883
2877
 
2884
- async def loadStormPkg(self, pkgdef):
2878
+ # N.B. This function is intentionally not async in order to prevent possible user race conditions for code
2879
+ # executing outside of the nexus lock.
2880
+ def loadStormPkg(self, pkgdef):
2885
2881
  '''
2886
2882
  Load a storm package into the storm library for this cortex.
2887
2883
 
@@ -2911,7 +2907,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2911
2907
  self.stormmods = stormmods
2912
2908
 
2913
2909
  for cdef in cmds:
2914
- await self._setStormCmd(cdef)
2910
+ self._setStormCmd(cdef)
2915
2911
 
2916
2912
  for gdef in pkgdef.get('graphs', ()):
2917
2913
  gdef = copy.deepcopy(gdef)
@@ -2937,7 +2933,9 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2937
2933
  await self.fire('core:pkg:onload:complete', pkg=name)
2938
2934
  self.schedCoro(_onload())
2939
2935
 
2940
- async def _dropStormPkg(self, pkgdef):
2936
+ # N.B. This function is intentionally not async in order to prevent possible user race conditions for code
2937
+ # executing outside of the nexus lock.
2938
+ def _dropStormPkg(self, pkgdef):
2941
2939
  '''
2942
2940
  Reverse the process of loadStormPkg()
2943
2941
  '''
@@ -2948,7 +2946,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2948
2946
 
2949
2947
  for cdef in pkgdef.get('commands', ()):
2950
2948
  name = cdef.get('name')
2951
- await self._popStormCmd(name)
2949
+ self._popStormCmd(name)
2952
2950
 
2953
2951
  pkgname = pkgdef.get('name')
2954
2952
 
@@ -4435,7 +4433,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
4435
4433
 
4436
4434
  async def _trySetStormCmd(self, name, cdef):
4437
4435
  try:
4438
- await self._setStormCmd(cdef)
4436
+ self._setStormCmd(cdef)
4439
4437
  except (asyncio.CancelledError, Exception):
4440
4438
  logger.exception(f'Storm command load failed: {name}')
4441
4439
 
synapse/exc.py CHANGED
@@ -56,9 +56,14 @@ class SynErr(Exception):
56
56
  self.errinfo[name] = valu
57
57
  self._setExcMesg()
58
58
 
59
+ def update(self, items: dict):
60
+ '''Update multiple items in the errinfo dict at once.'''
61
+ self.errinfo.update(**items)
62
+ self._setExcMesg()
63
+
59
64
  class StormRaise(SynErr):
60
65
  '''
61
- This represents a user provided exception inside of a Storm runtime. It requires a errname key.
66
+ This represents a user provided exception raised in the Storm runtime. It requires a errname key.
62
67
  '''
63
68
  def __init__(self, *args, **info):
64
69
  SynErr.__init__(self, *args, **info)
synapse/lib/agenda.py CHANGED
@@ -5,9 +5,7 @@ import asyncio
5
5
  import logging
6
6
  import calendar
7
7
  import datetime
8
- import functools
9
8
  import itertools
10
- import collections
11
9
  from datetime import timezone as tz
12
10
  from collections.abc import Iterable, Mapping
13
11
 
synapse/lib/ast.py CHANGED
@@ -59,7 +59,8 @@ class AstNode:
59
59
  }
60
60
 
61
61
  def addExcInfo(self, exc):
62
- exc.errinfo['highlight'] = self.getPosInfo()
62
+ if 'highlight' not in exc.errinfo:
63
+ exc.set('highlight', self.getPosInfo())
63
64
  return exc
64
65
 
65
66
  def repr(self):
@@ -3554,7 +3555,10 @@ class VarDeref(Value):
3554
3555
 
3555
3556
  valu = s_stormtypes.fromprim(base, path=path)
3556
3557
  with s_scope.enter({'runt': runt}):
3557
- return await valu.deref(name)
3558
+ try:
3559
+ return await valu.deref(name)
3560
+ except s_exc.SynErr as e:
3561
+ raise self.kids[1].addExcInfo(e)
3558
3562
 
3559
3563
  class FuncCall(Value):
3560
3564
 
@@ -3575,10 +3579,23 @@ class FuncCall(Value):
3575
3579
  kwargs = {k: v for (k, v) in await self.kids[2].compute(runt, path)}
3576
3580
 
3577
3581
  with s_scope.enter({'runt': runt}):
3578
- retn = func(*argv, **kwargs)
3579
- if s_coro.iscoro(retn):
3580
- return await retn
3581
- return retn
3582
+ try:
3583
+ retn = func(*argv, **kwargs)
3584
+ if s_coro.iscoro(retn):
3585
+ return await retn
3586
+ return retn
3587
+
3588
+ except TypeError as e:
3589
+ mesg = str(e)
3590
+ if (funcpath := getattr(func, '_storm_funcpath', None)) is not None:
3591
+ mesg = f"{funcpath}(){mesg.split(')', 1)[1]}"
3592
+
3593
+ raise self.addExcInfo(s_exc.StormRuntimeError(mesg=mesg))
3594
+
3595
+ except s_exc.SynErr as e:
3596
+ if getattr(func, '_storm_runtime_lib_func', None) is not None:
3597
+ e.errinfo.pop('highlight', None)
3598
+ raise self.addExcInfo(e)
3582
3599
 
3583
3600
  class DollarExpr(Value):
3584
3601
  '''
@@ -4887,8 +4904,9 @@ class Function(AstNode):
4887
4904
 
4888
4905
  @s_stormtypes.stormfunc(readonly=True)
4889
4906
  async def realfunc(*args, **kwargs):
4890
- return await self.callfunc(runt, argdefs, args, kwargs)
4907
+ return await self.callfunc(runt, argdefs, args, kwargs, realfunc._storm_funcpath)
4891
4908
 
4909
+ realfunc._storm_funcpath = self.name
4892
4910
  await runt.setVar(self.name, realfunc)
4893
4911
 
4894
4912
  count = 0
@@ -4910,7 +4928,7 @@ class Function(AstNode):
4910
4928
  # var scope validation occurs in the sub-runtime
4911
4929
  pass
4912
4930
 
4913
- async def callfunc(self, runt, argdefs, args, kwargs):
4931
+ async def callfunc(self, runt, argdefs, args, kwargs, funcpath):
4914
4932
  '''
4915
4933
  Execute a function call using the given runtime.
4916
4934
 
@@ -4921,7 +4939,7 @@ class Function(AstNode):
4921
4939
 
4922
4940
  argcount = len(args) + len(kwargs)
4923
4941
  if argcount > len(argdefs):
4924
- mesg = f'{self.name}() takes {len(argdefs)} arguments but {argcount} were provided'
4942
+ mesg = f'{funcpath}() takes {len(argdefs)} arguments but {argcount} were provided'
4925
4943
  raise self.kids[1].addExcInfo(s_exc.StormRuntimeError(mesg=mesg))
4926
4944
 
4927
4945
  # Fill in the positional arguments
@@ -4935,7 +4953,7 @@ class Function(AstNode):
4935
4953
  valu = kwargs.pop(name, s_common.novalu)
4936
4954
  if valu is s_common.novalu:
4937
4955
  if defv is s_common.novalu:
4938
- mesg = f'{self.name}() missing required argument {name}'
4956
+ mesg = f'{funcpath}() missing required argument {name}'
4939
4957
  raise self.kids[1].addExcInfo(s_exc.StormRuntimeError(mesg=mesg))
4940
4958
  valu = defv
4941
4959
 
@@ -4946,11 +4964,11 @@ class Function(AstNode):
4946
4964
  # used a kwarg not defined.
4947
4965
  kwkeys = list(kwargs.keys())
4948
4966
  if kwkeys[0] in posnames:
4949
- mesg = f'{self.name}() got multiple values for parameter {kwkeys[0]}'
4967
+ mesg = f'{funcpath}() got multiple values for parameter {kwkeys[0]}'
4950
4968
  raise self.kids[1].addExcInfo(s_exc.StormRuntimeError(mesg=mesg))
4951
4969
 
4952
4970
  plural = 's' if len(kwargs) > 1 else ''
4953
- mesg = f'{self.name}() got unexpected keyword argument{plural}: {",".join(kwkeys)}'
4971
+ mesg = f'{funcpath}() got unexpected keyword argument{plural}: {",".join(kwkeys)}'
4954
4972
  raise self.kids[1].addExcInfo(s_exc.StormRuntimeError(mesg=mesg))
4955
4973
 
4956
4974
  assert len(mergargs) == len(argdefs)