synapse 2.184.0__py311-none-any.whl → 2.185.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.

synapse/cortex.py CHANGED
@@ -946,7 +946,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
946
946
  self._exthttpapicache = s_cache.FixedCache(self._getHttpExtApiByPath, size=1000)
947
947
  self._initCortexExtHttpApi()
948
948
 
949
- self.model = s_datamodel.Model()
949
+ self.model = s_datamodel.Model(core=self)
950
950
 
951
951
  await self._bumpCellVers('cortex:extmodel', (
952
952
  (1, self._migrateTaxonomyIface),
synapse/datamodel.py CHANGED
@@ -482,8 +482,9 @@ class Model:
482
482
  '''
483
483
  The data model used by a Cortex hypergraph.
484
484
  '''
485
- def __init__(self):
485
+ def __init__(self, core=None):
486
486
 
487
+ self.core = core
487
488
  self.types = {} # name: Type()
488
489
  self.forms = {} # name: Form()
489
490
  self.props = {} # (form,name): Prop() and full: Prop()
@@ -653,13 +654,16 @@ class Model:
653
654
  self.formprefixcache[prefix] = forms
654
655
  return forms
655
656
 
656
- def reqProp(self, name):
657
+ def reqProp(self, name, extra=None):
657
658
  prop = self.prop(name)
658
659
  if prop is not None:
659
660
  return prop
660
661
 
661
- mesg = f'No property named {name}.'
662
- raise s_exc.NoSuchProp(mesg=mesg, name=name)
662
+ exc = s_exc.NoSuchProp.init(name)
663
+ if extra is not None:
664
+ exc = extra(exc)
665
+
666
+ raise exc
663
667
 
664
668
  def reqUniv(self, name):
665
669
  prop = self.univ(name)
@@ -1082,6 +1086,30 @@ class Model:
1082
1086
  self.props[prop.full] = prop
1083
1087
  return prop
1084
1088
 
1089
+ def _prepFormIface(self, form, iface):
1090
+
1091
+ template = iface.get('template', {})
1092
+ template.update(form.type.info.get('template', {}))
1093
+
1094
+ def convert(item):
1095
+
1096
+ if isinstance(item, str):
1097
+
1098
+ if item == '$self':
1099
+ return form.name
1100
+
1101
+ return item.format(**template)
1102
+
1103
+ if isinstance(item, dict):
1104
+ return {convert(k): convert(v) for (k, v) in item.items()}
1105
+
1106
+ if isinstance(item, (list, tuple)):
1107
+ return tuple([convert(v) for v in item])
1108
+
1109
+ return item
1110
+
1111
+ return convert(iface)
1112
+
1085
1113
  def _addFormIface(self, form, name, subifaces=None):
1086
1114
 
1087
1115
  iface = self.ifaces.get(name)
@@ -1094,9 +1122,14 @@ class Model:
1094
1122
  mesg = f'Form {form.name} depends on deprecated interface {name} which will be removed in 3.0.0'
1095
1123
  logger.warning(mesg)
1096
1124
 
1125
+ iface = self._prepFormIface(form, iface)
1126
+
1097
1127
  for propname, typedef, propinfo in iface.get('props', ()):
1098
- if typedef[0] == '$self':
1099
- typedef = (form.name, typedef[1])
1128
+
1129
+ # allow form props to take precedence
1130
+ if form.prop(propname) is not None:
1131
+ continue
1132
+
1100
1133
  prop = self._addFormProp(form, propname, typedef, propinfo)
1101
1134
  self.ifaceprops[f'{name}:{propname}'].append(prop.full)
1102
1135
 
@@ -1123,6 +1156,8 @@ class Model:
1123
1156
  if (iface := self.ifaces.get(name)) is None:
1124
1157
  return
1125
1158
 
1159
+ iface = self._prepFormIface(form, iface)
1160
+
1126
1161
  for propname, typedef, propinfo in iface.get('props', ()):
1127
1162
  fullprop = f'{form.name}:{propname}'
1128
1163
  self.delFormProp(form.name, propname)
synapse/lib/ast.py CHANGED
@@ -1426,6 +1426,32 @@ class LiftOper(Oper):
1426
1426
  self.astinfo = astinfo
1427
1427
  self.reverse = True
1428
1428
 
1429
+ def getPivNames(self, runt, prop, pivs):
1430
+ pivnames = []
1431
+ typename = prop.type.name
1432
+ for piv in pivs:
1433
+ pivprop = runt.model.reqProp(f'{typename}:{piv}', extra=self.kids[0].addExcInfo)
1434
+ pivnames.append(pivprop.full)
1435
+ typename = pivprop.type.name
1436
+
1437
+ return pivnames
1438
+
1439
+ async def pivlift(self, runt, props, pivnames, genr):
1440
+
1441
+ async def pivvals(prop, pivgenr):
1442
+ async for node in pivgenr:
1443
+ async for pivo in runt.snap.nodesByPropValu(prop, '=', node.ndef[1], reverse=self.reverse):
1444
+ yield pivo
1445
+
1446
+ for pivname in pivnames[-2::-1]:
1447
+ genr = pivvals(pivname, genr)
1448
+
1449
+ async for node in genr:
1450
+ valu = node.ndef[1]
1451
+ for prop in props:
1452
+ async for node in runt.snap.nodesByPropValu(prop.full, '=', valu, reverse=self.reverse):
1453
+ yield node
1454
+
1429
1455
  async def run(self, runt, genr):
1430
1456
 
1431
1457
  if self.isRuntSafe(runt):
@@ -1584,30 +1610,52 @@ class LiftByArray(LiftOper):
1584
1610
  cmpr = await self.kids[1].compute(runt, path)
1585
1611
  valu = await s_stormtypes.tostor(await self.kids[2].compute(runt, path))
1586
1612
 
1587
- prop = runt.model.props.get(name)
1588
- if prop is not None:
1589
- async for node in runt.snap.nodesByPropArray(name, cmpr, valu, reverse=self.reverse):
1590
- yield node
1591
- return
1613
+ pivs = None
1614
+ if name.find('::') != -1:
1615
+ parts = name.split('::')
1616
+ name, pivs = parts[0], parts[1:]
1592
1617
 
1593
- proplist = runt.model.ifaceprops.get(name)
1594
- if proplist is None:
1595
- raise self.kids[0].addExcInfo(s_exc.NoSuchProp.init(name))
1618
+ if (prop := runt.model.props.get(name)) is not None:
1619
+ props = (prop,)
1620
+ else:
1621
+ proplist = runt.model.ifaceprops.get(name)
1622
+ if proplist is None:
1623
+ raise self.kids[0].addExcInfo(s_exc.NoSuchProp.init(name))
1596
1624
 
1597
- props = []
1598
- for propname in proplist:
1599
- props.append(runt.model.props.get(propname))
1625
+ props = []
1626
+ for propname in proplist:
1627
+ props.append(runt.model.props.get(propname))
1600
1628
 
1601
- relname = props[0].name
1602
- def cmprkey(node):
1603
- return node.props.get(relname)
1629
+ try:
1630
+ if pivs is not None:
1631
+ pivnames = self.getPivNames(runt, props[0], pivs)
1604
1632
 
1605
- genrs = []
1606
- for prop in props:
1607
- genrs.append(runt.snap.nodesByPropArray(prop.full, cmpr, valu, reverse=self.reverse))
1633
+ genr = runt.snap.nodesByPropArray(pivnames[-1], cmpr, valu, reverse=self.reverse)
1634
+ async for node in self.pivlift(runt, props, pivnames, genr):
1635
+ yield node
1636
+ return
1608
1637
 
1609
- async for node in s_common.merggenr2(genrs, cmprkey, reverse=self.reverse):
1610
- yield node
1638
+ if len(props) == 1:
1639
+ async for node in runt.snap.nodesByPropArray(name, cmpr, valu, reverse=self.reverse):
1640
+ yield node
1641
+ return
1642
+
1643
+ relname = props[0].name
1644
+ def cmprkey(node):
1645
+ return node.props.get(relname)
1646
+
1647
+ genrs = []
1648
+ for prop in props:
1649
+ genrs.append(runt.snap.nodesByPropArray(prop.full, cmpr, valu, reverse=self.reverse))
1650
+
1651
+ async for node in s_common.merggenr2(genrs, cmprkey, reverse=self.reverse):
1652
+ yield node
1653
+
1654
+ except s_exc.BadTypeValu as e:
1655
+ raise self.kids[2].addExcInfo(e)
1656
+
1657
+ except s_exc.SynErr as e:
1658
+ raise self.addExcInfo(e)
1611
1659
 
1612
1660
  class LiftTagProp(LiftOper):
1613
1661
  '''
@@ -1839,6 +1887,11 @@ class LiftPropBy(LiftOper):
1839
1887
  if not isinstance(valu, s_node.Node):
1840
1888
  valu = await s_stormtypes.tostor(valu)
1841
1889
 
1890
+ pivs = None
1891
+ if name.find('::') != -1:
1892
+ parts = name.split('::')
1893
+ name, pivs = parts[0], parts[1:]
1894
+
1842
1895
  prop = runt.model.props.get(name)
1843
1896
  if prop is not None:
1844
1897
  props = (prop,)
@@ -1852,6 +1905,14 @@ class LiftPropBy(LiftOper):
1852
1905
  props.append(runt.model.props.get(propname))
1853
1906
 
1854
1907
  try:
1908
+ if pivs is not None:
1909
+ pivnames = self.getPivNames(runt, props[0], pivs)
1910
+
1911
+ genr = runt.snap.nodesByPropValu(pivnames[-1], cmpr, valu, reverse=self.reverse)
1912
+ async for node in self.pivlift(runt, props, pivnames, genr):
1913
+ yield node
1914
+ return
1915
+
1855
1916
  if len(props) == 1:
1856
1917
  prop = props[0]
1857
1918
  async for node in runt.snap.nodesByPropValu(prop.full, cmpr, valu, reverse=self.reverse):
@@ -3794,7 +3855,7 @@ class ExprDict(Value):
3794
3855
 
3795
3856
  def prepare(self):
3796
3857
  self.const = None
3797
- if all(isinstance(k, Const) for k in self.kids):
3858
+ if all(isinstance(k, Const) and not isinstance(k, EmbedQuery) for k in self.kids):
3798
3859
  valu = {}
3799
3860
  for i in range(0, len(self.kids), 2):
3800
3861
  valu[self.kids[i].value()] = self.kids[i + 1].value()
@@ -3824,7 +3885,7 @@ class ExprList(Value):
3824
3885
 
3825
3886
  def prepare(self):
3826
3887
  self.const = None
3827
- if all(isinstance(k, Const) for k in self.kids):
3888
+ if all(isinstance(k, Const) and not isinstance(k, EmbedQuery) for k in self.kids):
3828
3889
  self.const = s_msgpack.en([k.value() for k in self.kids])
3829
3890
 
3830
3891
  async def compute(self, runt, path):
synapse/lib/auth.py CHANGED
@@ -264,6 +264,19 @@ class Auth(s_nexus.Pusher):
264
264
  def _getRoleIden(self, name):
265
265
  return self.roleidenbyname.get(name)
266
266
 
267
+ # TODO convert getUserByName() and getRoleByName()
268
+ # back from async? These were plumbed to avoid infecting
269
+ # type norm/repr functions with async...
270
+ def _getRoleByName(self, name):
271
+ roleiden = self.roleidenbynamecache.get(name)
272
+ if roleiden is not None:
273
+ return self.role(roleiden)
274
+
275
+ def _getUserByName(self, name):
276
+ useriden = self.useridenbynamecache.get(name)
277
+ if useriden is not None:
278
+ return self.user(useriden)
279
+
267
280
  @s_nexus.Pusher.onPushAuto('user:profile:set')
268
281
  async def setUserProfileValu(self, iden, name, valu):
269
282
  user = await self.reqUser(iden)
synapse/lib/cell.py CHANGED
@@ -2118,6 +2118,13 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2118
2118
  Hand off leadership to a mirror in a transactional fashion.
2119
2119
  '''
2120
2120
  _dispname = f' ahaname={self.conf.get("aha:name")}' if self.conf.get('aha:name') else ''
2121
+
2122
+ if not self.isactive:
2123
+ mesg = f'HANDOFF: {_dispname} is not the current leader and cannot handoff leadership to' \
2124
+ f' {s_urlhelp.sanitizeUrl(turl)}.'
2125
+ logger.error(mesg)
2126
+ raise s_exc.BadState(mesg=mesg, turl=turl, cursvc=_dispname)
2127
+
2121
2128
  logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
2122
2129
  async with await s_telepath.openurl(turl) as cell:
2123
2130
 
@@ -4439,6 +4446,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4439
4446
  Returns:
4440
4447
  Dict: A Dictionary of metadata.
4441
4448
  '''
4449
+
4450
+ mirror = self.conf.get('mirror')
4451
+ if mirror is not None:
4452
+ mirror = s_urlhelp.sanitizeUrl(mirror)
4453
+
4442
4454
  ret = {
4443
4455
  'synapse': {
4444
4456
  'commit': s_version.commit,
@@ -4458,6 +4470,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4458
4470
  'cellvers': dict(self.cellvers.items()),
4459
4471
  'nexsindx': await self.getNexsIndx(),
4460
4472
  'uplink': self.nexsroot.miruplink.is_set(),
4473
+ 'mirror': mirror,
4461
4474
  'aha': {
4462
4475
  'name': self.conf.get('aha:name'),
4463
4476
  'leader': self.conf.get('aha:leader'),
synapse/lib/modules.py CHANGED
@@ -2,6 +2,7 @@
2
2
  Module which implements the synapse module API/convention.
3
3
  '''
4
4
  coremods = (
5
+ 'synapse.models.doc.DocModule',
5
6
  'synapse.models.dns.DnsModule',
6
7
  'synapse.models.orgs.OuModule',
7
8
  'synapse.models.syn.SynModule',
synapse/lib/parser.py CHANGED
@@ -46,6 +46,7 @@ terminalEnglishMap = {
46
46
  'DOT': '.',
47
47
  'DOUBLEQUOTEDSTRING': 'double-quoted string',
48
48
  'ELIF': 'elif',
49
+ 'EMBEDPROPS': 'absolute property name with embed properties',
49
50
  'EQNOSPACE': '=',
50
51
  'EQSPACE': '=',
51
52
  'EQUAL': '=',
synapse/lib/snap.py CHANGED
@@ -1692,6 +1692,7 @@ class Snap(s_base.Base):
1692
1692
 
1693
1693
  todo = s_common.todo('runRuntLift', full, valu, cmpr, self.view.iden)
1694
1694
  async for sode in self.core.dyniter('cortex', todo):
1695
+ await asyncio.sleep(0)
1695
1696
 
1696
1697
  node = s_node.Node(self, sode)
1697
1698
  node.isrunt = True
synapse/lib/storm.lark CHANGED
@@ -222,9 +222,9 @@ liftreverse: _REVERSE "(" (liftformtag | liftpropby | liftprop | liftbyarray | l
222
222
  _DEREF.3: /\*(?=\$)/
223
223
 
224
224
  liftformtag: ((PROPS | UNIVNAME | WILDPROPS) | _DEREF _varvalu) tagname [_cmpr _valu]
225
- liftpropby: ((PROPS | UNIVNAME) | _DEREF _varvalu) _cmpr _valu
225
+ liftpropby: ((PROPS | EMBEDPROPS | UNIVNAME) | _DEREF _varvalu) _cmpr _valu
226
226
  liftprop: ((PROPS | UNIVNAME | WILDPROPS) | _DEREF _varvalu)
227
- liftbyarray: ((PROPS | UNIVNAME) | _DEREF _varvalu) "*[" _safe_cmpr _valu "]"
227
+ liftbyarray: ((PROPS | EMBEDPROPS | UNIVNAME) | _DEREF _varvalu) "*[" _safe_cmpr _valu "]"
228
228
  lifttagtag: (_HASH | _HASHSPACE) tagname [_cmpr _valu]
229
229
  liftbytag: (tagname | tagnamewithspace) [_cmpr _valu]
230
230
  liftbytagprop: (tagprop | tagpropwithspace) [_cmpr _valu]
@@ -321,10 +321,10 @@ _valu: _basevalu | NONQUOTEWORD
321
321
  evalvalu: _valu
322
322
  exprdict: "{" ((_exprvalu | VARTOKN) (":" | _EXPRCOLONNOSPACE) (_exprvalu | VARTOKN) ("," (_exprvalu | VARTOKN) (":" | _EXPRCOLONNOSPACE) (_exprvalu | VARTOKN))* ","? )? "}"
323
323
  exprlist: "[" ((_exprvalu | VARTOKN) ("," (_exprvalu | VARTOKN))* ","? )? "]"
324
- // Just like _valu, but doesn't allow valu lists or unquoted strings or queries
324
+ // Just like _valu, but doesn't allow valu lists or unquoted strings
325
325
  _exprvalu: NUMBER | HEXNUMBER | OCTNUMBER | BOOL | NULL | exprlist | exprdict | _exprvarvalu | exprrelpropvalu
326
326
  | exprunivpropvalu | exprtagvalu | exprtagpropvalu | TRIPLEQUOTEDSTRING | DOUBLEQUOTEDSTRING
327
- | SINGLEQUOTEDSTRING | formatstring | _innerdollarexprs
327
+ | SINGLEQUOTEDSTRING | formatstring | _innerdollarexprs | embedquery
328
328
 
329
329
  // Expr versions of rules to avoid invalid state merges
330
330
  _innerdollarexprs: "$"? innerdollaroper
@@ -454,8 +454,14 @@ formatstring: "`" (_formatexpr | FORMATTEXT)* "`"
454
454
 
455
455
  // Must be kept consistent with same regexes in synapse/lib/grammar.py
456
456
  // A full property. Must contain at least 1 colon
457
- PROPS.2: /[a-z_][a-z0-9_]*(:[a-z0-9_]+)+([:.][a-z0-9_]+)*(?![:.a-z0-9_\-])/
458
- WILDPROPS.2: /[a-z_][a-z0-9_]*(:\*|(:[a-z0-9_]+)+([:.][a-z0-9_]+)*:?\*)(?![:.a-z0-9_\-\[])/
457
+ PROPS.3: /[a-z_][a-z0-9_]*(:[a-z0-9_]+)+([:.][a-z0-9_]+)*(?![:.a-z0-9_\-])/
458
+
459
+ // A full property containing a wildcard
460
+ WILDPROPS.3: /[a-z_][a-z0-9_]*(:\*|(:[a-z0-9_]+)+([:.][a-z0-9_]+)*:?\*)(?![:.a-z0-9_\-\[])/
461
+
462
+ // A full property with embed properties
463
+ EMBEDPROPS.2: /[a-z_][a-z0-9_]*(:[a-z0-9_]+)+((\:\:|\:|\.)[a-z0-9_]+)*(?![:.a-z0-9_\-])/
464
+
459
465
  // A universal property
460
466
  UNIVNAME.2: /(?<=^|[\s\|\{\(\[+=-])\.[a-z_][a-z0-9_]*([:.][a-z0-9_]+)*/
461
467
  univprop: UNIVNAME | "." _varvalu
synapse/lib/storm.py CHANGED
@@ -1778,8 +1778,10 @@ class StormDmon(s_base.Base):
1778
1778
 
1779
1779
  text = self.ddef.get('storm')
1780
1780
  opts = self.ddef.get('stormopts', {})
1781
- vars = opts.setdefault('vars', {})
1781
+
1782
+ vars = await s_stormtypes.toprim(opts.get('vars', {}), use_list=True)
1782
1783
  vars.setdefault('auto', {'iden': self.iden, 'type': 'dmon'})
1784
+ opts['vars'] = vars
1783
1785
 
1784
1786
  viewiden = opts.get('view')
1785
1787
 
@@ -2732,15 +2734,28 @@ class Parser:
2732
2734
  self.exited = True
2733
2735
  return False
2734
2736
 
2737
+ def _wrap_text(self, text, width):
2738
+ lines, curline, curlen = [], [], 0
2739
+ for word in text.split():
2740
+ if curlen + len(word) + bool(curline) > width:
2741
+ lines.append(' '.join(curline))
2742
+ curline, curlen = [word], len(word)
2743
+ else:
2744
+ curline.append(word)
2745
+ curlen += len(word) + bool(curline)
2746
+ if curline:
2747
+ lines.append(' '.join(curline))
2748
+ return lines
2749
+
2735
2750
  def _print_optarg(self, names, argdef):
2736
2751
 
2737
2752
  dest = self._get_dest_str(argdef)
2738
2753
  oact = argdef.get('action', 'store')
2739
2754
 
2740
2755
  if oact in ('store_true', 'store_false'):
2741
- base = f' {names[0]}'.ljust(30)
2756
+ base = f' {names[0]}'
2742
2757
  else:
2743
- base = f' {names[0]} {dest}'.ljust(30)
2758
+ base = f' {names[0]} {dest}'
2744
2759
 
2745
2760
  defval = argdef.get('default', s_common.novalu)
2746
2761
  choices = argdef.get('choices')
@@ -2748,12 +2763,14 @@ class Parser:
2748
2763
 
2749
2764
  if defval is not s_common.novalu and oact not in ('store_true', 'store_false'):
2750
2765
  if isinstance(defval, (tuple, list, dict)):
2751
- defval = pprint.pformat(defval, indent=34, width=100)
2752
- if '\n' in defval:
2753
- defval = '\n' + defval
2766
+ defval_ls = pprint.pformat(defval, width=120).split('\n')
2767
+ defval = '\n'.join(ln.strip() for ln in defval_ls)
2754
2768
 
2755
2769
  if choices is None:
2756
- helpstr = f'{helpstr} (default: {defval})'
2770
+ if (lambda tst: '\n' in tst if isinstance(tst, str) else False)(defval):
2771
+ helpstr = f'{helpstr} (default: \n{defval})'
2772
+ else:
2773
+ helpstr = f'{helpstr} (default: {defval})'
2757
2774
  else:
2758
2775
  cstr = ', '.join(str(c) for c in choices)
2759
2776
  helpstr = f'{helpstr} (default: {defval}, choices: {cstr})'
@@ -2762,7 +2779,26 @@ class Parser:
2762
2779
  cstr = ', '.join(str(c) for c in choices)
2763
2780
  helpstr = f'{helpstr} (choices: {cstr})'
2764
2781
 
2765
- self._printf(f'{base}: {helpstr}')
2782
+ helplst = helpstr.split('\n')
2783
+ if helplst and not helplst[0].strip():
2784
+ helplst = helplst[1:]
2785
+ min_space = min((len(ln) - len(ln.lstrip()) for ln in helplst if ln.strip()), default=0)
2786
+
2787
+ base_w = 32
2788
+ wrap_w = 120 - base_w
2789
+
2790
+ first = helplst[0][min_space:]
2791
+ wrap_first = self._wrap_text(first, wrap_w)
2792
+ self._printf(f'{base:<{base_w-2}}: {wrap_first[0]}')
2793
+
2794
+ for ln in wrap_first[1:]: self._printf(f'{"":<{base_w}}{ln}')
2795
+ for ln in helplst[1:]:
2796
+ lead_s = len(ln) - len(ln.lstrip())
2797
+ rel_ind = lead_s - min_space
2798
+ ind = ' ' * (base_w + rel_ind)
2799
+ wrapped = self._wrap_text(ln.lstrip(), wrap_w - rel_ind)
2800
+ for wl in wrapped:
2801
+ self._printf(f'{ind}{wl}')
2766
2802
 
2767
2803
  def _print_posarg(self, name, argdef):
2768
2804
  dest = self._get_dest_str(argdef)
@@ -5232,7 +5268,7 @@ class BackgroundCmd(Cmd):
5232
5268
  async for item in genr:
5233
5269
  yield item
5234
5270
 
5235
- runtprims = await s_stormtypes.toprim(self.runt.getScopeVars())
5271
+ runtprims = await s_stormtypes.toprim(self.runt.getScopeVars(), use_list=True)
5236
5272
  runtvars = {k: v for (k, v) in runtprims.items() if s_msgpack.isok(v)}
5237
5273
 
5238
5274
  opts = {
@@ -29,6 +29,7 @@ TerminalPygMap = {
29
29
  'DOT': p_t.Punctuation,
30
30
  'DOUBLEQUOTEDSTRING': p_t.Literal.String,
31
31
  'ELIF': p_t.Keyword,
32
+ 'EMBEDPROPS': p_t.Name,
32
33
  'EQNOSPACE': p_t.Punctuation,
33
34
  'EQSPACE': p_t.Punctuation,
34
35
  'EQUAL': p_t.Punctuation,
@@ -486,7 +486,10 @@ _DefaultConfig = {
486
486
  },
487
487
  }
488
488
 
489
- def _validateConfig(core, config):
489
+ perm_maxsize = ('storm', 'lib', 'stix', 'export', 'maxsize')
490
+ def _validateConfig(runt, config):
491
+
492
+ core = runt.snap.core
490
493
 
491
494
  maxsize = config.get('maxsize', 10000)
492
495
 
@@ -506,9 +509,10 @@ def _validateConfig(core, config):
506
509
  mesg = f'STIX Bundle config maxsize option must be an integer.'
507
510
  raise s_exc.BadConfValu(mesg=mesg)
508
511
 
509
- if maxsize > 10000:
510
- mesg = f'STIX Bundle config maxsize option must be <= 10000.'
511
- raise s_exc.BadConfValu(mesg=mesg)
512
+ if maxsize > 10000 and not runt.allowed(perm_maxsize):
513
+ permstr = '.'.join(perm_maxsize)
514
+ mesg = f'Setting STIX export maxsize > 10,000 requires permission: {permstr}'
515
+ raise s_exc.AuthDeny(mesg=mesg, perm=permstr)
512
516
 
513
517
  formmaps = config.get('forms')
514
518
  if formmaps is None:
@@ -1040,6 +1044,11 @@ class LibStixExport(s_stormtypes.Lib):
1040
1044
  '''
1041
1045
  A Storm Library for exporting to STIX version 2.1 CS02.
1042
1046
  '''
1047
+ _storm_lib_perms = (
1048
+ {'perm': ('storm', 'lib', 'stix', 'export', 'maxsize'), 'gate': 'cortex',
1049
+ 'desc': 'Controls the ability to specify a STIX export bundle maxsize of greater than 10,000.'},
1050
+ )
1051
+
1043
1052
  _storm_locals = ( # type: ignore
1044
1053
  {
1045
1054
  'name': 'bundle',
@@ -1172,7 +1181,7 @@ class LibStixExport(s_stormtypes.Lib):
1172
1181
  config = _DefaultConfig
1173
1182
 
1174
1183
  config = await s_stormtypes.toprim(config)
1175
- _validateConfig(self.runt.snap.core, config)
1184
+ _validateConfig(self.runt, config)
1176
1185
 
1177
1186
  return StixBundle(self, self.runt, config)
1178
1187