synapse 2.160.0__py311-none-any.whl → 2.162.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 (71) hide show
  1. synapse/cortex.py +12 -7
  2. synapse/daemon.py +7 -2
  3. synapse/lib/agenda.py +8 -2
  4. synapse/lib/aha.py +4 -4
  5. synapse/lib/ast.py +3 -3
  6. synapse/lib/cell.py +20 -1
  7. synapse/lib/hiveauth.py +1 -1
  8. synapse/lib/httpapi.py +5 -0
  9. synapse/lib/layer.py +21 -1
  10. synapse/lib/nexus.py +9 -5
  11. synapse/lib/node.py +3 -4
  12. synapse/lib/rstorm.py +16 -0
  13. synapse/lib/schemas.py +2 -1
  14. synapse/lib/snap.py +20 -11
  15. synapse/lib/storm.py +19 -3
  16. synapse/lib/stormhttp.py +14 -2
  17. synapse/lib/stormlib/easyperm.py +5 -2
  18. synapse/lib/stormlib/gen.py +119 -44
  19. synapse/lib/stormlib/stix.py +6 -3
  20. synapse/lib/stormlib/vault.py +32 -15
  21. synapse/lib/stormtypes.py +187 -21
  22. synapse/lib/trigger.py +2 -0
  23. synapse/lib/version.py +2 -2
  24. synapse/lib/view.py +42 -10
  25. synapse/models/inet.py +9 -0
  26. synapse/models/infotech.py +28 -26
  27. synapse/models/orgs.py +3 -0
  28. synapse/models/proj.py +9 -2
  29. synapse/models/risk.py +32 -0
  30. synapse/telepath.py +6 -2
  31. synapse/tests/files/rstorm/testsvc.py +8 -1
  32. synapse/tests/files/stormpkg/testpkg.yaml +4 -0
  33. synapse/tests/test_axon.py +4 -4
  34. synapse/tests/test_cortex.py +66 -8
  35. synapse/tests/test_daemon.py +19 -0
  36. synapse/tests/test_lib_agenda.py +8 -0
  37. synapse/tests/test_lib_aha.py +18 -3
  38. synapse/tests/test_lib_ast.py +38 -16
  39. synapse/tests/test_lib_cell.py +3 -0
  40. synapse/tests/test_lib_grammar.py +4 -4
  41. synapse/tests/test_lib_httpapi.py +59 -0
  42. synapse/tests/test_lib_nexus.py +63 -0
  43. synapse/tests/test_lib_rstorm.py +38 -2
  44. synapse/tests/test_lib_snap.py +10 -0
  45. synapse/tests/test_lib_storm.py +61 -20
  46. synapse/tests/test_lib_stormhttp.py +21 -21
  47. synapse/tests/test_lib_stormlib_auth.py +3 -3
  48. synapse/tests/test_lib_stormlib_cell.py +1 -1
  49. synapse/tests/test_lib_stormlib_cortex.py +50 -2
  50. synapse/tests/test_lib_stormlib_gen.py +77 -0
  51. synapse/tests/test_lib_stormlib_json.py +2 -2
  52. synapse/tests/test_lib_stormlib_macro.py +1 -1
  53. synapse/tests/test_lib_stormlib_modelext.py +37 -37
  54. synapse/tests/test_lib_stormlib_oauth.py +20 -20
  55. synapse/tests/test_lib_stormlib_stix.py +3 -1
  56. synapse/tests/test_lib_stormlib_vault.py +1 -1
  57. synapse/tests/test_lib_stormtypes.py +159 -47
  58. synapse/tests/test_lib_stormwhois.py +1 -1
  59. synapse/tests/test_lib_trigger.py +11 -11
  60. synapse/tests/test_lib_view.py +23 -1
  61. synapse/tests/test_model_crypto.py +1 -1
  62. synapse/tests/test_model_inet.py +6 -0
  63. synapse/tests/test_model_orgs.py +2 -1
  64. synapse/tests/test_model_proj.py +6 -0
  65. synapse/tests/test_model_risk.py +10 -0
  66. synapse/tests/test_tools_storm.py +1 -1
  67. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/METADATA +5 -3
  68. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/RECORD +71 -71
  69. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/LICENSE +0 -0
  70. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/WHEEL +0 -0
  71. {synapse-2.160.0.dist-info → synapse-2.162.0.dist-info}/top_level.txt +0 -0
@@ -105,10 +105,32 @@ class LibGen(s_stormtypes.Lib):
105
105
  'desc': 'Type normalization will fail silently instead of raising an exception.'},
106
106
  ),
107
107
  'returns': {'type': 'node', 'desc': 'A lang:language node with the given code.'}}},
108
+ {'name': 'itAvScanResultByTarget',
109
+ 'desc': 'Returns an it:av:scan:result node by deconflicting with a target and signature name, adding the node if it does not exist.',
110
+ 'type': {'type': 'function', '_funcname': '_storm_query',
111
+ 'args': (
112
+ {'name': 'form', 'type': 'str', 'desc': 'The target form.'},
113
+ {'name': 'value', 'type': 'str', 'desc': 'The target value.'},
114
+ {'name': 'signame', 'type': 'str', 'desc': 'The signature name.'},
115
+ {'name': 'scanner', 'type': 'str', 'default': None,
116
+ 'desc': 'An optional scanner software name to include in deconfliction.'},
117
+ {'name': 'time', 'type': 'time', 'default': None,
118
+ 'desc': 'An optional time when the scan was run to include in the deconfliction.'},
119
+ {'name': 'try', 'type': 'boolean', 'default': False,
120
+ 'desc': 'Type normalization will fail silently instead of raising an exception.'},
121
+ ),
122
+ 'returns': {'type': 'node', 'desc': 'An it:av:scan:result node.'}}},
108
123
  )
109
124
  _storm_lib_path = ('gen',)
110
125
 
111
126
  _storm_query = '''
127
+ function __maybeCast(try, type, valu) {
128
+ if $try {
129
+ return($lib.trycast($type, $valu))
130
+ }
131
+ return(($lib.true, $lib.cast($type, $valu)))
132
+ }
133
+
112
134
  function orgIdType(name) {
113
135
  ou:id:type:name=$name
114
136
  return($node)
@@ -128,12 +150,8 @@ class LibGen(s_stormtypes.Lib):
128
150
  }
129
151
 
130
152
  function orgByName(name, try=$lib.false) {
131
- if $try {
132
- ($ok, $name) = $lib.trycast(ou:name, $name)
133
- if (not $ok) { return() }
134
- } else {
135
- $name = $lib.cast(ou:name, $name)
136
- }
153
+ ($ok, $name) = $__maybeCast($try, ou:name, $name)
154
+ if (not $ok) { return() }
137
155
 
138
156
  ou:name=$name -> ou:org
139
157
  return($node)
@@ -143,12 +161,8 @@ class LibGen(s_stormtypes.Lib):
143
161
  }
144
162
 
145
163
  function orgByFqdn(fqdn, try=$lib.false) {
146
- if $try {
147
- ($ok, $fqdn) = $lib.trycast("inet:fqdn", $fqdn)
148
- if (not $ok) { return() }
149
- } else {
150
- $fqdn = $lib.cast(inet:fqdn, $fqdn)
151
- }
164
+ ($ok, $fqdn) = $__maybeCast($try, inet:fqdn, $fqdn)
165
+ if (not $ok) { return() }
152
166
 
153
167
  inet:fqdn=$fqdn -> ou:org
154
168
  return($node)
@@ -180,12 +194,8 @@ class LibGen(s_stormtypes.Lib):
180
194
  }
181
195
 
182
196
  function newsByUrl(url, try=$lib.false) {
183
- if $try {
184
- ($ok, $url) = $lib.trycast(inet:url, $url)
185
- if (not $ok) { return() }
186
- } else {
187
- $url = $lib.cast(inet:url, $url)
188
- }
197
+ ($ok, $url) = $__maybeCast($try, inet:url, $url)
198
+ if (not $ok) { return() }
189
199
 
190
200
  media:news:url=$url
191
201
  return($node)
@@ -205,12 +215,8 @@ class LibGen(s_stormtypes.Lib):
205
215
  }
206
216
 
207
217
  function vulnByCve(cve, try=$lib.false) {
208
- if $try {
209
- ($ok, $cve) = $lib.trycast("it:sec:cve", $cve)
210
- if (not $ok) { return() }
211
- } else {
212
- $cve = $lib.cast(it:sec:cve, $cve)
213
- }
218
+ ($ok, $cve) = $__maybeCast($try, it:sec:cve, $cve)
219
+ if (not $ok) { return() }
214
220
 
215
221
  risk:vuln:cve=$cve
216
222
  return($node)
@@ -258,14 +264,11 @@ class LibGen(s_stormtypes.Lib):
258
264
  }
259
265
 
260
266
  function psContactByEmail(type, email, try=$lib.false) {
267
+ ($ok, $email) = $__maybeCast($try, inet:email, $email)
268
+ if (not $ok) { return() }
261
269
 
262
- if $try {
263
- ($ok, $email) = $lib.trycast("inet:email", $email)
264
- if (not $ok) { return() }
265
- } else {
266
- $type = $lib.cast(ps:contact:type:taxonomy, $type)
267
- $email = $lib.cast(inet:email, $email)
268
- }
270
+ ($ok, $type) = $__maybeCast($try, ps:contact:type:taxonomy, $type)
271
+ if (not $ok) { return() }
269
272
 
270
273
  ps:contact:email = $email
271
274
  +:type = $type
@@ -279,12 +282,8 @@ class LibGen(s_stormtypes.Lib):
279
282
  }
280
283
 
281
284
  function polCountryByIso2(iso2, try=$lib.false) {
282
- if $try {
283
- ($ok, $iso2) = $lib.trycast("pol:iso2", $iso2)
284
- if (not $ok) { return() }
285
- } else {
286
- $iso2 = $lib.cast(pol:iso2, $iso2)
287
- }
285
+ ($ok, $iso2) = $__maybeCast($try, pol:iso2, $iso2)
286
+ if (not $ok) { return() }
288
287
 
289
288
  pol:country:iso2=$iso2
290
289
  return($node)
@@ -313,13 +312,8 @@ class LibGen(s_stormtypes.Lib):
313
312
  }
314
313
 
315
314
  function langByCode(code, try=$lib.false) {
316
-
317
- if $try {
318
- ($ok, $code) = $lib.trycast(lang:code, $code)
319
- if (not $ok) { return() }
320
- } else {
321
- $code = $lib.cast(lang:code, $code)
322
- }
315
+ ($ok, $code) = $__maybeCast($try, lang:code, $code)
316
+ if (not $ok) { return() }
323
317
 
324
318
  lang:language:code=$code
325
319
  return($node)
@@ -339,6 +333,54 @@ class LibGen(s_stormtypes.Lib):
339
333
  [ ou:campaign=(gen, name, reporter, $name, $reporter) :name=$name :reporter:name=$reporter ]
340
334
  return($node)
341
335
  }
336
+
337
+ function itAvScanResultByTarget(form, value, signame, scanner=$lib.null, time=$lib.null, try=$lib.false) {
338
+
339
+ ($ok, $value) = $__maybeCast($try, $form, $value)
340
+ if (not $ok) { return() }
341
+
342
+ switch $form {
343
+ "file:bytes": { $tprop = target:file }
344
+ "inet:fqdn": { $tprop = target:fqdn }
345
+ "inet:ipv4": { $tprop = target:ipv4 }
346
+ "inet:ipv6": { $tprop = target:ipv6 }
347
+ "inet:url": { $tprop = target:url }
348
+ "it:exec:proc": { $tprop = target:proc }
349
+ "it:host": { $tprop = target:host }
350
+ *: {
351
+ $lib.raise(BadArg, `Unsupported target form {$form}`)
352
+ }
353
+ }
354
+
355
+ ($ok, $signame) = $__maybeCast($try, it:av:signame, $signame)
356
+ if (not $ok) { return() }
357
+
358
+ if ($scanner != $lib.null) {
359
+ ($ok, $scanner) = $__maybeCast($try, it:prod:softname, $scanner)
360
+ if (not $ok) { return() }
361
+ }
362
+
363
+ if ($time != $lib.null) {
364
+ ($ok, $time) = $__maybeCast($try, time, $time)
365
+ if (not $ok) { return() }
366
+ }
367
+
368
+ $tlift = `it:av:scan:result:{$tprop}`
369
+
370
+ *$tlift=$value +:signame=$signame
371
+ if ($time != $lib.null) { +:time=$time }
372
+ if ($scanner != $lib.null) { +:scanner:name=$scanner }
373
+ return($node)
374
+
375
+ [ it:av:scan:result=(gen, target, $form, $value, $signame, $scanner, $time)
376
+ :signame=$signame
377
+ :$tprop=$value
378
+ :scanner:name?=$scanner
379
+ :time?=$time
380
+ ]
381
+
382
+ return($node)
383
+ }
342
384
  '''
343
385
 
344
386
  stormcmds = (
@@ -507,4 +549,37 @@ stormcmds = (
507
549
  ),
508
550
  'storm': 'yield $lib.gen.langByName($cmdopts.name)',
509
551
  },
552
+ # todo: remove it:av:filehit example in 3.x.x
553
+ {
554
+ 'name': 'gen.it.av.scan.result',
555
+ 'descr': '''
556
+ Lift (or create) the it:av:scan:result node by deconflicting the target and signature time.
557
+
558
+ The scan time and scanner name may also optionally be provided for deconfliction.
559
+
560
+ Examples:
561
+
562
+ // Yield the it:av:scan:result node for an FQDN and signature name
563
+ gen.it.av.scan.result inet:fqdn vertex.link foosig
564
+
565
+ // Also deconflict by scanner name and scan time
566
+ gen.it.av.scan.result inet:fqdn fqdn vertex.link foosig --scanner-name barscanner --time 2022-11-03
567
+
568
+ // Generate an it:av:scan:result node from an it:av:filehit node
569
+ it:av:filehit#foo | gen.it.av.scan.result file:bytes :file :sig:name
570
+ ''',
571
+ 'cmdargs': (
572
+ ('form', {'help': 'The target form.'}),
573
+ ('value', {'help': 'The target value.'}),
574
+ ('signame', {'help': 'The signature name.'}),
575
+ ('--scanner-name', {'help': 'An optional scanner software name to include in deconfliction.'}),
576
+ ('--time', {'help': 'An optional time when the scan was run to include in the deconfliction.'}),
577
+ ('--try', {'help': 'Type normalization will fail silently instead of raising an exception.',
578
+ 'action': 'store_true'}),
579
+ ),
580
+ 'storm': '''
581
+ yield $lib.gen.itAvScanResultByTarget($cmdopts.form, $cmdopts.value, $cmdopts.signame,
582
+ scanner=$cmdopts.scanner_name, time=$cmdopts.time, try=$cmdopts.try)
583
+ ''',
584
+ }
510
585
  )
@@ -247,7 +247,7 @@ _DefaultConfig = {
247
247
  'name': '{+:name return(:name)} return($node.repr())',
248
248
  'size': '+:size return(:size)',
249
249
  'hashes': '''
250
- init { $dict = $lib.dict() }
250
+ init { $dict = ({}) }
251
251
  { +:md5 $dict.MD5 = :md5 }
252
252
  { +:sha1 $dict."SHA-1" = :sha1 }
253
253
  { +:sha256 $dict."SHA-256" = :sha256 }
@@ -392,7 +392,7 @@ _DefaultConfig = {
392
392
  'description': 'if (:desc) { return (:desc) }',
393
393
  'created': 'return($lib.stix.export.timestamp(.created))',
394
394
  'modified': 'return($lib.stix.export.timestamp(.created))',
395
- 'external_references': 'if :cve { $cve=:cve $cve=$cve.upper() $list=$lib.list($lib.dict(source_name=cve, external_id=$cve)) return($list) }'
395
+ 'external_references': 'if :cve { $cve=:cve $cve=$cve.upper() $list=$lib.list(({"source_name": "cve", "external_id": $cve})) return($list) }'
396
396
  },
397
397
  'rels': (
398
398
 
@@ -1405,7 +1405,10 @@ class StixBundle(s_stormtypes.Prim):
1405
1405
 
1406
1406
  async def _callStorm(self, text, node):
1407
1407
 
1408
- opts = {'vars': {'bundle': self}}
1408
+ varz = self.runt.getScopeVars()
1409
+ varz['bundle'] = self
1410
+
1411
+ opts = {'vars': varz}
1409
1412
  query = await self.runt.snap.core.getStormQuery(text)
1410
1413
  async with self.runt.getCmdRuntime(query, opts=opts) as runt:
1411
1414
 
@@ -477,7 +477,8 @@ class LibVault(s_stormtypes.Lib):
477
477
  name = await s_stormtypes.tostr(name)
478
478
 
479
479
  vault = self.runt.snap.core.reqVaultByName(name)
480
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ)
480
+ mesg = f'User requires read permission on vault: {name}.'
481
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ, mesg=mesg)
481
482
 
482
483
  return Vault(self.runt, vault.get('iden'))
483
484
 
@@ -485,7 +486,8 @@ class LibVault(s_stormtypes.Lib):
485
486
  iden = await s_stormtypes.tostr(iden)
486
487
 
487
488
  vault = self.runt.snap.core.reqVault(iden)
488
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ)
489
+ mesg = f'User requires read permission on vault: {iden}.'
490
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ, mesg=mesg)
489
491
 
490
492
  return Vault(self.runt, iden)
491
493
 
@@ -496,7 +498,10 @@ class LibVault(s_stormtypes.Lib):
496
498
  vault = self.runt.snap.core.getVaultByType(vtype, self.runt.user.iden, scope)
497
499
  if not vault:
498
500
  return None
499
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ)
501
+
502
+ iden = vault.get('iden')
503
+ mesg = f'User requires read permission on vault: {iden}.'
504
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_READ, mesg=mesg)
500
505
 
501
506
  return Vault(self.runt, vault.get('iden'))
502
507
 
@@ -523,12 +528,14 @@ class VaultConfigs(s_stormtypes.Prim):
523
528
  self.runt = runt
524
529
 
525
530
  vault = self.runt.snap.core.reqVault(valu)
526
- s_stormtypes.confirmEasyPerm(vault, self._vault_perm)
531
+ mesg = f'User requires {s_cell.permnames.get(self._vault_perm)} permission on vault: {valu}.'
532
+ s_stormtypes.confirmEasyPerm(vault, self._vault_perm, mesg=mesg)
527
533
 
528
534
  @s_stormtypes.stormfunc(readonly=False)
529
535
  async def setitem(self, name, valu):
530
536
  vault = self.runt.snap.core.reqVault(self.valu)
531
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT)
537
+ mesg = f'User requires edit permission on vault: {self.valu}.'
538
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT, mesg=mesg)
532
539
 
533
540
  name = await s_stormtypes.tostr(name)
534
541
 
@@ -541,7 +548,8 @@ class VaultConfigs(s_stormtypes.Prim):
541
548
 
542
549
  async def deref(self, name):
543
550
  vault = self.runt.snap.core.reqVault(self.valu)
544
- s_stormtypes.confirmEasyPerm(vault, self._vault_perm)
551
+ mesg = f'User requires {s_cell.permnames.get(self._vault_perm)} permission on vault: {self.valu}.'
552
+ s_stormtypes.confirmEasyPerm(vault, self._vault_perm, mesg=mesg)
545
553
 
546
554
  name = await s_stormtypes.tostr(name)
547
555
 
@@ -550,7 +558,8 @@ class VaultConfigs(s_stormtypes.Prim):
550
558
 
551
559
  async def iter(self):
552
560
  vault = self.runt.snap.core.reqVault(self.valu)
553
- self.runt.confirmEasyPerm(vault, self._vault_perm)
561
+ mesg = f'User requires {s_cell.permnames.get(self._vault_perm)} permission on vault: {self.valu}.'
562
+ self.runt.confirmEasyPerm(vault, self._vault_perm, mesg=mesg)
554
563
 
555
564
  data = vault.get(self._vault_field_name)
556
565
 
@@ -564,13 +573,15 @@ class VaultConfigs(s_stormtypes.Prim):
564
573
 
565
574
  async def stormrepr(self):
566
575
  vault = self.runt.snap.core.reqVault(self.valu)
567
- s_stormtypes.confirmEasyPerm(vault, self._vault_perm)
576
+ mesg = f'User requires {s_cell.permnames.get(self._vault_perm)} permission on vault: {self.valu}.'
577
+ s_stormtypes.confirmEasyPerm(vault, self._vault_perm, mesg=mesg)
568
578
  data = vault.get(self._vault_field_name)
569
579
  return repr(data)
570
580
 
571
581
  def value(self):
572
582
  vault = self.runt.snap.core.reqVault(self.valu)
573
- self.runt.confirmEasyPerm(vault, self._vault_perm)
583
+ mesg = f'User requires {s_cell.permnames.get(self._vault_perm)} permission on vault: {self.valu}.'
584
+ self.runt.confirmEasyPerm(vault, self._vault_perm, mesg=mesg)
574
585
  return vault.get(self._vault_field_name)
575
586
 
576
587
  class VaultSecrets(VaultConfigs):
@@ -580,7 +591,8 @@ class VaultSecrets(VaultConfigs):
580
591
  @s_stormtypes.stormfunc(readonly=False)
581
592
  async def setitem(self, name, valu):
582
593
  vault = self.runt.snap.core.reqVault(self.valu)
583
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT)
594
+ mesg = f'User requires edit permission on vault: {self.valu}.'
595
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT, mesg=mesg)
584
596
 
585
597
  name = await s_stormtypes.tostr(name)
586
598
 
@@ -686,7 +698,8 @@ class Vault(s_stormtypes.Prim):
686
698
 
687
699
  async def _storName(self, name):
688
700
  vault = self.runt.snap.core.reqVault(self.valu)
689
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT)
701
+ mesg = f'User requires edit permission on vault: {self.valu}.'
702
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT, mesg=mesg)
690
703
 
691
704
  name = await s_stormtypes.tostr(name)
692
705
 
@@ -703,7 +716,8 @@ class Vault(s_stormtypes.Prim):
703
716
  async def _storConfigs(self, configs):
704
717
  configs = await s_stormtypes.toprim(configs)
705
718
  vault = self.runt.snap.core.reqVault(self.valu)
706
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT)
719
+ mesg = f'User requires edit permission on vault: {self.valu}.'
720
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT, mesg=mesg)
707
721
  return await self.runt.snap.core.replaceVaultConfigs(self.valu, configs)
708
722
 
709
723
  async def _gtorSecrets(self):
@@ -716,7 +730,8 @@ class Vault(s_stormtypes.Prim):
716
730
  async def _storSecrets(self, secrets):
717
731
  secrets = await s_stormtypes.toprim(secrets)
718
732
  vault = self.runt.snap.core.reqVault(self.valu)
719
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT)
733
+ mesg = f'User requires edit permission on vault: {self.valu}.'
734
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_EDIT, mesg=mesg)
720
735
  return await self.runt.snap.core.replaceVaultSecrets(self.valu, secrets)
721
736
 
722
737
  async def _gtorPermissions(self):
@@ -725,7 +740,8 @@ class Vault(s_stormtypes.Prim):
725
740
 
726
741
  async def _methSetPerm(self, iden, level):
727
742
  vault = self.runt.snap.core.reqVault(self.valu)
728
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_ADMIN)
743
+ mesg = f'User requires admin permission on vault: {self.valu}.'
744
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_ADMIN, mesg=mesg)
729
745
 
730
746
  iden = await s_stormtypes.tostr(iden)
731
747
  level = await s_stormtypes.toint(level)
@@ -734,7 +750,8 @@ class Vault(s_stormtypes.Prim):
734
750
 
735
751
  async def _methDelete(self):
736
752
  vault = self.runt.snap.core.reqVault(self.valu)
737
- s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_ADMIN)
753
+ mesg = f'User requires admin permission on vault: {self.valu}.'
754
+ s_stormtypes.confirmEasyPerm(vault, s_cell.PERM_ADMIN, mesg=mesg)
738
755
 
739
756
  return await self.runt.snap.core.delVault(self.valu)
740
757