synapse 2.152.0__py311-none-any.whl → 2.154.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 (87) hide show
  1. synapse/axon.py +19 -16
  2. synapse/cortex.py +203 -15
  3. synapse/exc.py +0 -2
  4. synapse/lib/ast.py +42 -23
  5. synapse/lib/autodoc.py +2 -2
  6. synapse/lib/cache.py +16 -1
  7. synapse/lib/cell.py +5 -5
  8. synapse/lib/httpapi.py +198 -2
  9. synapse/lib/layer.py +5 -2
  10. synapse/lib/modelrev.py +36 -3
  11. synapse/lib/node.py +2 -5
  12. synapse/lib/parser.py +1 -1
  13. synapse/lib/schemas.py +51 -0
  14. synapse/lib/snap.py +10 -0
  15. synapse/lib/storm.lark +24 -4
  16. synapse/lib/storm.py +98 -19
  17. synapse/lib/storm_format.py +1 -1
  18. synapse/lib/stormhttp.py +11 -4
  19. synapse/lib/stormlib/auth.py +16 -2
  20. synapse/lib/stormlib/backup.py +1 -0
  21. synapse/lib/stormlib/basex.py +2 -0
  22. synapse/lib/stormlib/cell.py +7 -0
  23. synapse/lib/stormlib/compression.py +3 -0
  24. synapse/lib/stormlib/cortex.py +1168 -0
  25. synapse/lib/stormlib/ethereum.py +1 -0
  26. synapse/lib/stormlib/graph.py +2 -0
  27. synapse/lib/stormlib/hashes.py +5 -0
  28. synapse/lib/stormlib/hex.py +6 -0
  29. synapse/lib/stormlib/infosec.py +6 -1
  30. synapse/lib/stormlib/ipv6.py +1 -0
  31. synapse/lib/stormlib/iters.py +58 -1
  32. synapse/lib/stormlib/json.py +5 -0
  33. synapse/lib/stormlib/mime.py +1 -0
  34. synapse/lib/stormlib/model.py +19 -3
  35. synapse/lib/stormlib/modelext.py +1 -0
  36. synapse/lib/stormlib/notifications.py +2 -0
  37. synapse/lib/stormlib/pack.py +2 -0
  38. synapse/lib/stormlib/random.py +1 -0
  39. synapse/lib/stormlib/smtp.py +0 -7
  40. synapse/lib/stormlib/stats.py +223 -0
  41. synapse/lib/stormlib/stix.py +8 -0
  42. synapse/lib/stormlib/storm.py +1 -0
  43. synapse/lib/stormlib/version.py +3 -0
  44. synapse/lib/stormlib/xml.py +3 -0
  45. synapse/lib/stormlib/yaml.py +2 -0
  46. synapse/lib/stormtypes.py +250 -170
  47. synapse/lib/trigger.py +180 -4
  48. synapse/lib/types.py +1 -1
  49. synapse/lib/version.py +2 -2
  50. synapse/lib/view.py +55 -6
  51. synapse/models/inet.py +21 -6
  52. synapse/models/orgs.py +48 -2
  53. synapse/models/risk.py +126 -2
  54. synapse/models/syn.py +6 -0
  55. synapse/tests/files/stormpkg/badapidef.yaml +13 -0
  56. synapse/tests/files/stormpkg/storm/modules/apimod +10 -0
  57. synapse/tests/files/stormpkg/testpkg.yaml +23 -0
  58. synapse/tests/test_axon.py +7 -2
  59. synapse/tests/test_cortex.py +231 -35
  60. synapse/tests/test_lib_ast.py +138 -43
  61. synapse/tests/test_lib_autodoc.py +1 -1
  62. synapse/tests/test_lib_modelrev.py +9 -0
  63. synapse/tests/test_lib_node.py +55 -0
  64. synapse/tests/test_lib_storm.py +14 -1
  65. synapse/tests/test_lib_stormhttp.py +65 -6
  66. synapse/tests/test_lib_stormlib_auth.py +12 -3
  67. synapse/tests/test_lib_stormlib_cortex.py +1327 -0
  68. synapse/tests/test_lib_stormlib_iters.py +116 -0
  69. synapse/tests/test_lib_stormlib_stats.py +187 -0
  70. synapse/tests/test_lib_stormlib_storm.py +8 -0
  71. synapse/tests/test_lib_stormsvc.py +24 -1
  72. synapse/tests/test_lib_stormtypes.py +124 -69
  73. synapse/tests/test_lib_trigger.py +315 -0
  74. synapse/tests/test_lib_view.py +1 -2
  75. synapse/tests/test_model_base.py +26 -0
  76. synapse/tests/test_model_inet.py +22 -0
  77. synapse/tests/test_model_orgs.py +28 -0
  78. synapse/tests/test_model_risk.py +73 -0
  79. synapse/tests/test_tools_autodoc.py +25 -0
  80. synapse/tests/test_tools_genpkg.py +9 -3
  81. synapse/tests/utils.py +39 -0
  82. synapse/tools/autodoc.py +42 -2
  83. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/METADATA +2 -2
  84. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/RECORD +87 -79
  85. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
  86. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
  87. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
@@ -552,3 +552,318 @@ class TrigTest(s_t_utils.SynTest):
552
552
 
553
553
  trig = await core.callStorm('return ($lib.trigger.get($iden))', opts=opts)
554
554
  self.eq(trig.get('user'), derp.iden)
555
+
556
+ async def test_trigger_edges(self):
557
+ async with self.getTestCore() as core:
558
+ view = await core.callStorm('return ($lib.view.get().fork().iden)')
559
+
560
+ await self.asyncraises(s_exc.SchemaViolation, core.nodes('''
561
+ $tdef = $lib.dict(
562
+ cond="edge:add",
563
+ form="test:int",
564
+ storm="[+#asdfasdf]"
565
+ )
566
+ $lib.trigger.add($tdef)
567
+ '''))
568
+
569
+ await self.asyncraises(s_exc.SchemaViolation, core.nodes('''
570
+ $tdef = $lib.dict(
571
+ cond="edge:add",
572
+ form="test:int",
573
+ storm="[+#asdfasdf]",
574
+ verb=$lib.null
575
+ )
576
+ $lib.trigger.add($tdef)
577
+ '''))
578
+
579
+ # edge:add
580
+ tdef = {
581
+ 'cond': 'edge:add',
582
+ 'verb': 'refs',
583
+ 'storm': '[ +#neato ] | spin | iden $auto.opts.n2iden | [ +#other ] | [ <(seen)+ { [ test:str=$auto.opts.verb ] } ]',
584
+ 'view': view,
585
+ }
586
+ await core.nodes('$lib.trigger.add($tdef)', opts={'vars': {'tdef': tdef}}) # only verb
587
+
588
+ opts = {'view': view}
589
+
590
+ await core.nodes('trigger.add edge:add --verb refs --form test:int --query { [ +#burrito ] }', opts=opts) # n1 + edge
591
+ await core.nodes('trigger.add edge:add --verb refs --n2form test:int --query { [ +#ping ]}', opts=opts) # edge + n2
592
+ await core.nodes('trigger.add edge:add --verb refs --form test:int --n2form test:int --query { [ +#pong ]}', opts=opts) # n1 + verb + n2
593
+
594
+ await core.nodes('[ test:str=foo <(refs)+ { [ test:str=bar ] } ]', opts=opts) # fire the verb-only trigger
595
+ await core.nodes('[ test:int=123 +(refs)> { [ test:str=biz ] } ]', opts=opts) # fire the n1 trigger and the verb trigger
596
+ await core.nodes('[ test:int=456 <(refs)+ { [ test:str=baz ] } ]', opts=opts) # fire the n2 trigger and the verb trigger
597
+ await core.nodes('[ test:int=789 +(refs)> { [ test:int=0 ] } ]', opts=opts) # FIRE ALL THE CANNONS
598
+
599
+ await core.nodes('[ test:int=9876 +(refs)> { [ test:int=54321 ]}]', opts=opts) # explicitly hit the cache
600
+
601
+ node = await core.nodes('test:int=0', opts=opts)
602
+ self.len(1, node)
603
+ self.isin('other', node[0].tags)
604
+
605
+ node = await core.nodes('test:int=123', opts=opts)
606
+ self.len(1, node)
607
+ self.isin('neato', node[0].tags)
608
+ self.isin('burrito', node[0].tags)
609
+
610
+ node = await core.nodes('test:int=456', opts=opts)
611
+ self.len(1, node)
612
+ self.isin('other', node[0].tags)
613
+
614
+ node = await core.nodes('test:int=789', opts=opts)
615
+ self.len(1, node)
616
+ self.isin('neato', node[0].tags)
617
+ self.isin('burrito', node[0].tags)
618
+ self.isin('ping', node[0].tags)
619
+ self.isin('pong', node[0].tags)
620
+
621
+ node = await core.nodes('test:str=foo', opts=opts)
622
+ self.len(1, node)
623
+ self.isin('other', node[0].tags)
624
+
625
+ node = await core.nodes('test:str=bar', opts=opts)
626
+ self.len(1, node)
627
+ self.isin('neato', node[0].tags)
628
+
629
+ node = await core.nodes('test:str=biz', opts=opts)
630
+ self.len(1, node)
631
+ self.isin('other', node[0].tags)
632
+
633
+ node = await core.nodes('test:str=baz', opts=opts)
634
+ self.len(1, node)
635
+ self.isin('ping', node[0].tags)
636
+ self.isin('neato', node[0].tags)
637
+
638
+ node = await core.nodes('test:int=9876', opts=opts)
639
+ self.len(1, node)
640
+ self.isin('neato', node[0].tags)
641
+ self.isin('burrito', node[0].tags)
642
+ self.isin('ping', node[0].tags)
643
+ self.isin('pong', node[0].tags)
644
+
645
+ # invalidate the cache
646
+ await core.nodes('trigger.add edge:add --verb refs --form test:int --n2form test:int --query { [ +#invalid ]}', opts=opts) # n1 + verb + n2
647
+
648
+ node = await core.nodes('[test:int=2468 <(refs)+ { [test:int=1357] }]', opts=opts)
649
+ self.notin('invalid', node[0].tags)
650
+
651
+ node = await core.nodes('test:int=1357', opts=opts)
652
+ self.isin('invalid', node[0].tags)
653
+
654
+ nodes = await core.nodes('test:str=refs -(seen)> *', opts=opts) # collates all the n2 nodes
655
+ ndefs = set([
656
+ ('test:int', 0),
657
+ ('test:int', 456),
658
+ ('test:str', 'foo'),
659
+ ('test:str', 'biz'),
660
+ ('test:int', 54321),
661
+ ('test:int', 2468)
662
+ ])
663
+ self.eq(ndefs, set([n.ndef for n in nodes]))
664
+
665
+ nodes = await core.nodes('syn:trigger:cond="edge:add"', opts=opts)
666
+ self.len(5, nodes)
667
+ n2 = 0
668
+ for n in nodes:
669
+ self.eq(n.props['verb'], 'refs')
670
+ if n.props.get('n2form') is not None:
671
+ n2 += 1
672
+ self.eq(n2, 3)
673
+
674
+ await core.nodes('for $trig in $lib.trigger.list() { $lib.trigger.del($trig.iden) }', opts=opts)
675
+ self.len(0, await core.nodes('syn:trigger', opts=opts))
676
+
677
+ # edge:del triggers
678
+ await core.nodes('trigger.add edge:del --verb refs --query { [ +#cookies ] | spin | iden $auto.opts.n2iden | [ +#milk ] }', opts=opts) # only edge
679
+ await core.nodes('trigger.add edge:del --verb refs --form test:int --query { [ +#cupcake ] }', opts=opts) # n1 form + edge
680
+ await core.nodes('trigger.add edge:del --verb refs --n2form test:int --query { [ +#icecream ] }', opts=opts) # edge + n2 form
681
+ await core.nodes('trigger.add edge:del --verb refs --form test:int --n2form test:int --query { [ +#croissant ] }', opts=opts) # n1 form + verb + n2 form
682
+
683
+ await core.nodes('test:str=foo [ <(refs)- { test:str=bar }]', opts=opts) # fire the verb-only trigger
684
+ await core.nodes('test:int=123 | edges.del *', opts=opts) # fire the n1 trigger and verb trigger
685
+ await core.nodes('test:int=456 | edges.del refs --n2', opts=opts) # fire the n2 trigger and verb trigger
686
+ await core.nodes('test:int=789 [ -(refs)> { test:int=0 } ]', opts=opts) # fire everything
687
+
688
+ await core.nodes('test:int=9876 [ -(refs)> { test:int=54321 } ]', opts=opts) # explicitly hit the cache
689
+
690
+ node = await core.nodes('test:int=0', opts=opts)
691
+ self.isin('milk', node[0].tags)
692
+
693
+ node = await core.nodes('test:int=123', opts=opts)
694
+ self.isin('cupcake', node[0].tags)
695
+ self.isin('cookies', node[0].tags)
696
+
697
+ # test:int=456 won't have anything on it, but test:str=baz will
698
+ node = await core.nodes('test:int=789', opts=opts)
699
+ self.isin('cookies', node[0].tags)
700
+ self.isin('icecream', node[0].tags)
701
+ self.isin('croissant', node[0].tags)
702
+ self.isin('cupcake', node[0].tags)
703
+
704
+ node = await core.nodes('test:str=foo', opts=opts)
705
+ self.isin('milk', node[0].tags)
706
+
707
+ node = await core.nodes('test:str=bar', opts=opts)
708
+ self.isin('cookies', node[0].tags)
709
+
710
+ node = await core.nodes('test:str=biz', opts=opts)
711
+ self.isin('milk', node[0].tags)
712
+
713
+ node = await core.nodes('test:str=baz', opts=opts)
714
+ self.isin('cookies', node[0].tags)
715
+ self.isin('icecream', node[0].tags)
716
+
717
+ node = await core.nodes('test:int=9876', opts=opts)
718
+ self.len(1, node)
719
+ self.isin('cookies', node[0].tags)
720
+ self.isin('icecream', node[0].tags)
721
+ self.isin('croissant', node[0].tags)
722
+ self.isin('cupcake', node[0].tags)
723
+
724
+ await core.nodes('trigger.add edge:del --verb refs --form test:int --n2form test:int --query { [ +#scone ] }', opts=opts) # n1 form + verb + n2 form
725
+ node = await core.nodes('test:int=1357 | [ -(refs)> { test:int=2468 } ]', opts=opts)
726
+ self.isin('scone', node[0].tags)
727
+
728
+ nodes = await core.nodes('syn:trigger:cond="edge:del"', opts=opts)
729
+ self.len(5, nodes)
730
+ n2 = 0
731
+ for n in nodes:
732
+ self.eq(n.props['verb'], 'refs')
733
+ if n.props.get('n2form') is not None:
734
+ n2 += 1
735
+ self.eq(n2, 3)
736
+
737
+ # make a pair of nodes in the base view, then the edge in the forked, and rip out one of the nodes
738
+ await core.nodes('[test:int=21701 test:int=23209]')
739
+ await core.nodes('test:int=21701 | [ <(refs)+ { test:int=23209 } ]', opts=opts)
740
+ await core.nodes('test:int=21701 | delnode')
741
+
742
+ await core.nodes('test:int=23209 | edges.del *', opts=opts)
743
+ node = await core.nodes('test:int=23209', opts=opts)
744
+ self.len(1, node)
745
+ self.isin('cookies', node[0].tags)
746
+ self.isin('cupcake', node[0].tags)
747
+ # the other two edge:del triggers cannot run because we can't get to n2 anymore
748
+
749
+ await core.nodes('for $trig in $lib.trigger.list() { $lib.trigger.del($trig.iden) }', opts=opts)
750
+ self.len(0, await core.nodes('syn:trigger', opts=opts))
751
+
752
+ async def test_trigger_edge_globs(self):
753
+ async with self.getTestCore() as core:
754
+ await core.nodes('trigger.add edge:add --verb foo* --query { [ +#foo ] | spin | iden $auto.opts.n2iden | [+#other] }')
755
+ await core.nodes('trigger.add edge:add --verb see* --form test:int --query { [ +#n1 ] }')
756
+ await core.nodes('trigger.add edge:add --verb r* --n2form test:int --query { [ +#n2 ] }')
757
+ await core.nodes('trigger.add edge:add --verb no** --form test:int --n2form test:str --query { [ +#both ] }')
758
+
759
+ async with core.enterMigrationMode():
760
+ nodes = await core.nodes('[test:int=123 +(foo:beep:boop)> { [test:str=neato] }]')
761
+ self.len(1, nodes)
762
+ self.notin('foo', nodes[0].tags)
763
+
764
+ nodes = await core.nodes('[test:int=123 +(foo:bar:baz)> { [test:str=neato] }]')
765
+ self.len(1, nodes)
766
+ self.isin('foo', nodes[0].tags)
767
+
768
+ nodes = await core.nodes('test:str=neato')
769
+ self.len(1, nodes)
770
+ self.isin('other', nodes[0].tags)
771
+
772
+ nodes = await core.nodes('[test:str=stuff +(see.saw)> { test:str=neato } ]')
773
+ self.len(1, nodes)
774
+ self.notin('n1', nodes[0].tags)
775
+
776
+ nodes = await core.nodes('[test:int=456 +(see.saw)> { test:str=neato } ]')
777
+ self.len(1, nodes)
778
+ self.isin('n1', nodes[0].tags)
779
+
780
+ nodes = await core.nodes('[test:str=neato +(ready)> { [ test:str=burrito ] } ]')
781
+ self.len(1, nodes)
782
+ self.notin('n2', nodes[0].tags)
783
+
784
+ nodes = await core.nodes('[test:int=456 +(ready)> { test:int=123 } ]')
785
+ self.len(1, nodes)
786
+ self.isin('n2', nodes[0].tags)
787
+
788
+ nodes = await core.nodes('[test:int=789 +(nope)> { test:int=123 } ]')
789
+ self.len(1, nodes)
790
+ self.notin('both', nodes[0].tags)
791
+
792
+ nodes = await core.nodes('[test:int=789 +(nope)> { test:str=burrito } ]')
793
+ self.len(1, nodes)
794
+ self.isin('both', nodes[0].tags)
795
+
796
+ await core.nodes('trigger.add edge:add --verb not* --form test:int --n2form test:str --query { [ +#cache.destroy ] }')
797
+
798
+ nodes = await core.nodes('[test:int=135 +(note)> { [ test:str=koolaidman ] } ]')
799
+ self.len(1, nodes)
800
+ self.isin('both', nodes[0].tags)
801
+ self.isin('cache.destroy', nodes[0].tags)
802
+
803
+ await core.nodes('for $trig in $lib.trigger.list() { $lib.trigger.del($trig.iden) }')
804
+ self.len(0, await core.nodes('syn:trigger'))
805
+
806
+ nodes = await core.nodes('[test:int=12345 +(note)> { [ test:str=scrambledeggs ] } ]')
807
+ self.len(1, nodes)
808
+ self.len(0, nodes[0].tags)
809
+
810
+ nodes = await core.nodes('[test:int=9876 +(foo:bar)> { test:str=neato }]')
811
+ self.len(1, nodes)
812
+ self.notin('foo', nodes[0].tags)
813
+
814
+ await core.nodes('trigger.add edge:del --verb foo* --query { [ +#del.none ] | spin | iden $auto.opts.n2iden | [+#del.other] }')
815
+ await core.nodes('trigger.add edge:del --verb see* --form test:int --query { [ +#del.one ] }')
816
+ await core.nodes('trigger.add edge:del --verb r* --n2form test:int --query { [ +#del.two ] }')
817
+ await core.nodes('trigger.add edge:del --verb no** --form test:int --n2form test:str --query { [ +#del.all ] }')
818
+
819
+ async with core.enterMigrationMode():
820
+ nodes = await core.nodes('test:int=123 | [ -(foo:beep:boop)> { test:str=neato } ]')
821
+ self.len(1, nodes)
822
+ self.notin('del.none', nodes[0].tags)
823
+
824
+ nodes = await core.nodes('test:int=123 | [ -(foo:bar:baz)> { test:str=neato } ]')
825
+ self.len(1, nodes)
826
+ self.isin('del.none', nodes[0].tags)
827
+
828
+ nodes = await core.nodes('test:str=neato')
829
+ self.len(1, nodes)
830
+ self.isin('del.other', nodes[0].tags)
831
+
832
+ nodes = await core.nodes('test:int=456 | [ -(see.saw)> {test:str=neato} ]')
833
+ self.len(1, nodes)
834
+ self.isin('del.one', nodes[0].tags)
835
+
836
+ nodes = await core.nodes('test:int=456 | [ -(ready)> {test:int=123}]')
837
+ self.len(1, nodes)
838
+ self.isin('del.two', nodes[0].tags)
839
+
840
+ nodes = await core.nodes('test:int=789 | [ -(nope)> { test:int=123 } ]')
841
+ self.len(1, nodes)
842
+ self.notin('del.all', nodes[0].tags)
843
+
844
+ nodes = await core.nodes('test:int=789 | [ -(nope)> { test:str=burrito } ]')
845
+ self.len(1, nodes)
846
+ self.isin('del.all', nodes[0].tags)
847
+
848
+ await core.nodes('trigger.add edge:del --verb no** --form test:int --n2form test:str --query { [ +#cleanup ] }')
849
+
850
+ nodes = await core.nodes('test:int=12345 | [ -(note)> { test:str=scrambledeggs } ]')
851
+ self.len(1, nodes)
852
+ self.isin('cleanup', nodes[0].tags)
853
+ self.isin('del.all', nodes[0].tags)
854
+
855
+ view = await core.callStorm('return ($lib.view.get().fork().iden)')
856
+ opts = {'view': view}
857
+ await core.nodes('trigger.add edge:del --verb no** --form test:str --query { [ +#coffee ] }', opts=opts)
858
+ await core.nodes('trigger.add edge:del --verb no** --form test:str --n2form test:str --query { [ +#oeis.a000668 ] }', opts=opts)
859
+
860
+ await core.nodes('[test:str=mersenne test:str=prime]')
861
+ await core.nodes('test:str=mersenne [ +(notes)> { test:str=prime } ]', opts=opts)
862
+ await core.nodes('test:str=prime | delnode')
863
+ node = await core.nodes('test:str=mersenne | edges.del *', opts=opts)
864
+ self.len(1, node)
865
+ self.len(1, node[0].tags)
866
+ self.isin('coffee', node[0].tags)
867
+
868
+ await core.nodes('for $trig in $lib.trigger.list() { $lib.trigger.del($trig.iden) }')
869
+ self.len(0, await core.nodes('syn:trigger'))
@@ -182,8 +182,7 @@ class ViewTest(s_t_utils.SynTest):
182
182
  tmpiden = tmplayr['iden']
183
183
  await self.asyncraises(s_exc.ReadOnlyLayer, core.view.addLayer(tmpiden))
184
184
  await self.asyncraises(s_exc.ReadOnlyLayer, view2.addLayer(tmpiden))
185
- await self.asyncraises(s_exc.ReadOnlyLayer, core.view.setLayers([tmpiden]))
186
- await self.asyncraises(s_exc.ReadOnlyLayer, view2.setLayers([tmpiden]))
185
+ await self.asyncraises(s_exc.BadArg, view2.setLayers([tmpiden]))
187
186
 
188
187
  # You can't merge a non-forked view
189
188
  await self.asyncraises(s_exc.SynErr, view2.core.view.merge())
@@ -351,3 +351,29 @@ class BaseTest(s_t_utils.SynTest):
351
351
  doc = ifdef.get('doc')
352
352
  self.nn(doc)
353
353
  self.ge(len(doc), 3)
354
+
355
+ async def test_model_doc_deprecated(self):
356
+
357
+ async with self.getTestCore() as core:
358
+
359
+ # Check properties that have "deprecated" in the doc string. Skip "isnow" because it's
360
+ # likely to have "deprecated" in the doc string due to what it does.
361
+ nodes = await core.nodes('syn:prop:doc~="(?i)deprecate"')
362
+ for node in nodes:
363
+ prop = core.model.prop(node.ndef[1])
364
+ if prop.name == 'isnow':
365
+ continue
366
+
367
+ self.true(prop.deprecated, msg=prop)
368
+
369
+ # Check types that have "deprecated" in the doc string.
370
+ nodes = await core.nodes('syn:type:doc="(?i)deprecate"')
371
+ for node in nodes:
372
+ typo = core.model.type(node.ndef[1])
373
+ self.true(typo.deprecated, msg=typo)
374
+
375
+ # Check forms that have "deprecated" in the doc string.
376
+ nodes = await core.nodes('syn:form:doc="(?i)deprecate"')
377
+ for node in nodes:
378
+ form = core.model.form(node.ndef[1])
379
+ self.true(form.deprecated, msg=form)
@@ -875,6 +875,18 @@ class InetModelTest(s_t_utils.SynTest):
875
875
  self.eq(2851995905, norm)
876
876
  self.eq(info.get('subs').get('type'), 'linklocal')
877
877
 
878
+ norm, info = t.norm('100.63.255.255')
879
+ self.eq(info.get('subs').get('type'), 'unicast')
880
+
881
+ norm, info = t.norm('100.64.0.0')
882
+ self.eq(info.get('subs').get('type'), 'shared')
883
+
884
+ norm, info = t.norm('100.127.255.255')
885
+ self.eq(info.get('subs').get('type'), 'shared')
886
+
887
+ norm, info = t.norm('100.128.0.0')
888
+ self.eq(info.get('subs').get('type'), 'unicast')
889
+
878
890
  # Don't allow invalid values
879
891
  with self.raises(s_exc.BadTypeValu):
880
892
  t.norm(0x00000000 - 1)
@@ -1332,6 +1344,10 @@ class InetModelTest(s_t_utils.SynTest):
1332
1344
  t = core.model.type(formname)
1333
1345
  self.raises(s_exc.BadTypeValu, t.norm, 'http:///wat')
1334
1346
  self.raises(s_exc.BadTypeValu, t.norm, 'wat') # No Protocol
1347
+ self.raises(s_exc.BadTypeValu, t.norm, "file://''") # Missing address/url
1348
+ self.raises(s_exc.BadTypeValu, t.norm, "file://#") # Missing address/url
1349
+ self.raises(s_exc.BadTypeValu, t.norm, "file://$") # Missing address/url
1350
+ self.raises(s_exc.BadTypeValu, t.norm, "file://%") # Missing address/url
1335
1351
 
1336
1352
  self.raises(s_exc.BadTypeValu, t.norm, 'www.google\udcfesites.com/hehe.asp')
1337
1353
  valu = t.norm('http://www.googlesites.com/hehe\udcfestuff.asp')
@@ -1440,6 +1456,12 @@ class InetModelTest(s_t_utils.SynTest):
1440
1456
  nodes = await core.nodes(q)
1441
1457
  self.len(0, nodes)
1442
1458
 
1459
+ nodes = await core.nodes('[ inet:url="https://+:80/woot" ]')
1460
+ self.len(1, nodes)
1461
+
1462
+ self.none(nodes[0].get('ipv4'))
1463
+ self.none(nodes[0].get('fqdn'))
1464
+
1443
1465
  async def test_url_file(self):
1444
1466
 
1445
1467
  async with self.getTestCore() as core:
@@ -586,6 +586,34 @@ class OuModelTest(s_t_utils.SynTest):
586
586
  self.len(1, nodes)
587
587
  self.len(1, nodes[0].get('industries'))
588
588
 
589
+ nodes = await core.nodes('''[ ou:requirement=50b757fafe4a839ec499023ebcffe7c0
590
+ :name="acquire pizza toppings"
591
+ :text="The team must acquire ANSI standard pizza toppings."
592
+ :goal={[ ou:goal=* :name=pizza ]}
593
+ :issuer={[ ps:contact=* :name=visi ]}
594
+ :assignee={ gen.ou.org.hq ledos }
595
+ :optional=(true)
596
+ :priority=highest
597
+ :issued=20120202
598
+ :period=(2023, ?)
599
+ :active=(true)
600
+ :deps=(*, *)
601
+ :deps:min=1
602
+ ]''')
603
+ self.len(1, nodes)
604
+ self.eq('acquire pizza toppings', nodes[0].get('name'))
605
+ self.eq('The team must acquire ANSI standard pizza toppings.', nodes[0].get('text'))
606
+ self.eq(1, nodes[0].get('deps:min'))
607
+ self.eq(50, nodes[0].get('priority'))
608
+ self.eq(True, nodes[0].get('optional'))
609
+ self.eq(1328140800000, nodes[0].get('issued'))
610
+ self.eq((1672531200000, 9223372036854775807), nodes[0].get('period'))
611
+
612
+ self.len(2, await core.nodes('ou:requirement=50b757fafe4a839ec499023ebcffe7c0 -> ou:requirement'))
613
+ self.len(1, await core.nodes('ou:requirement=50b757fafe4a839ec499023ebcffe7c0 -> ou:goal +:name=pizza'))
614
+ self.len(1, await core.nodes('ou:requirement=50b757fafe4a839ec499023ebcffe7c0 :issuer -> ps:contact +:name=visi'))
615
+ self.len(1, await core.nodes('ou:requirement=50b757fafe4a839ec499023ebcffe7c0 :assignee -> ps:contact +:orgname=ledos'))
616
+
589
617
  async def test_ou_code_prefixes(self):
590
618
  guid0 = s_common.guid()
591
619
  guid1 = s_common.guid()
@@ -280,6 +280,7 @@ class RiskModelTest(s_t_utils.SynTest):
280
280
  :url=https://vertex.link/alerts/WOOT-20
281
281
  :ext:id=WOOT-20
282
282
  :engine={[ it:prod:softver=* :name=visiware ]}
283
+ :host=*
283
284
  ]
284
285
  ''')
285
286
  self.len(1, nodes)
@@ -289,6 +290,8 @@ class RiskModelTest(s_t_utils.SynTest):
289
290
  self.eq(2554848000000, nodes[0].get('detected'))
290
291
  self.eq('WOOT-20', nodes[0].get('ext:id'))
291
292
  self.eq('https://vertex.link/alerts/WOOT-20', nodes[0].get('url'))
293
+ self.nn(nodes[0].get('host'))
294
+ self.len(1, await core.nodes('risk:alert -> it:host'))
292
295
  self.len(1, await core.nodes('risk:alert -> risk:vuln'))
293
296
  self.len(1, await core.nodes('risk:alert -> risk:attack'))
294
297
  self.len(1, await core.nodes('risk:alert :engine -> it:prod:softver'))
@@ -299,6 +302,8 @@ class RiskModelTest(s_t_utils.SynTest):
299
302
  :name = "Visi Wants Pizza"
300
303
  :desc = "Visi wants a pepperoni and mushroom pizza"
301
304
  :type = when.noms.attack
305
+ :url=https://vertex.link/pwned
306
+ :ext:id=PWN-00
302
307
  :reporter = *
303
308
  :reporter:name = vertex
304
309
  :severity = 10
@@ -324,6 +329,8 @@ class RiskModelTest(s_t_utils.SynTest):
324
329
  self.eq('Visi wants a pepperoni and mushroom pizza', nodes[0].get('desc'))
325
330
  self.eq('when.noms.attack.', nodes[0].get('type'))
326
331
  self.eq('vertex', nodes[0].get('reporter:name'))
332
+ self.eq('PWN-00', nodes[0].get('ext:id'))
333
+ self.eq('https://vertex.link/pwned', nodes[0].get('url'))
327
334
  self.nn(nodes[0].get('target'))
328
335
  self.nn(nodes[0].get('attacker'))
329
336
  self.nn(nodes[0].get('campaign'))
@@ -394,6 +401,72 @@ class RiskModelTest(s_t_utils.SynTest):
394
401
  self.len(1, nodes[0].get('techniques'))
395
402
  self.len(1, await core.nodes('risk:threat:merged:isnow -> risk:threat'))
396
403
 
404
+ nodes = await core.nodes('''[ risk:leak=*
405
+ :name="WikiLeaks ACME Leak"
406
+ :desc="WikiLeaks leaked ACME stuff."
407
+ :disclosed=20231102
408
+ :owner={ gen.ou.org.hq acme }
409
+ :leaker={ gen.ou.org.hq wikileaks }
410
+ :type=public
411
+ :goal={[ ou:goal=* :name=publicity ]}
412
+ :compromise={[ risk:compromise=* :target={ gen.ou.org.hq acme } ]}
413
+ :public=(true)
414
+ :public:url=https://wikileaks.org/acme
415
+ :reporter={ gen.ou.org vertex }
416
+ :reporter:name=vertex
417
+ ]''')
418
+ self.len(1, nodes)
419
+ self.eq('wikileaks acme leak', nodes[0].get('name'))
420
+ self.eq('WikiLeaks leaked ACME stuff.', nodes[0].get('desc'))
421
+ self.eq(1698883200000, nodes[0].get('disclosed'))
422
+ self.eq('public.', nodes[0].get('type'))
423
+ self.eq(1, nodes[0].get('public'))
424
+ self.eq('https://wikileaks.org/acme', nodes[0].get('public:url'))
425
+ self.eq('vertex', nodes[0].get('reporter:name'))
426
+
427
+ self.len(1, await core.nodes('risk:leak -> risk:leak:type:taxonomy'))
428
+ self.len(1, await core.nodes('risk:leak :owner -> ps:contact +:orgname=acme'))
429
+ self.len(1, await core.nodes('risk:leak :leaker -> ps:contact +:orgname=wikileaks'))
430
+ self.len(1, await core.nodes('risk:leak -> ou:goal +:name=publicity'))
431
+ self.len(1, await core.nodes('risk:leak -> risk:compromise :target -> ps:contact +:orgname=acme'))
432
+ self.len(1, await core.nodes('risk:leak :reporter -> ou:org +:name=vertex'))
433
+
434
+ nodes = await core.nodes('''[ risk:extortion=*
435
+ :demanded=20231102
436
+ :name="APT99 Extorted ACME"
437
+ :desc="APT99 extorted ACME for a zillion vertex coins."
438
+ :type=fingain
439
+ :attacker={[ ps:contact=* :name=agent99 ]}
440
+ :target={ gen.ou.org.hq acme }
441
+ :success=(true)
442
+ :enacted=(true)
443
+ :public=(true)
444
+ :public:url=https://apt99.com/acme
445
+ :compromise={[ risk:compromise=* :target={ gen.ou.org.hq acme } ]}
446
+ :demanded:payment:price=99.99
447
+ :demanded:payment:currency=VTC
448
+ :reporter={ gen.ou.org vertex }
449
+ :reporter:name=vertex
450
+ ]''')
451
+
452
+ self.len(1, nodes)
453
+ self.eq('apt99 extorted acme', nodes[0].get('name'))
454
+ self.eq('APT99 extorted ACME for a zillion vertex coins.', nodes[0].get('desc'))
455
+ self.eq(1698883200000, nodes[0].get('demanded'))
456
+ self.eq('fingain.', nodes[0].get('type'))
457
+ self.eq(1, nodes[0].get('public'))
458
+ self.eq(1, nodes[0].get('success'))
459
+ self.eq(1, nodes[0].get('enacted'))
460
+ self.eq('https://apt99.com/acme', nodes[0].get('public:url'))
461
+ self.eq('99.99', nodes[0].get('demanded:payment:price'))
462
+ self.eq('vtc', nodes[0].get('demanded:payment:currency'))
463
+ self.eq('vertex', nodes[0].get('reporter:name'))
464
+
465
+ self.len(1, await core.nodes('risk:extortion :target -> ps:contact +:orgname=acme'))
466
+ self.len(1, await core.nodes('risk:extortion :attacker -> ps:contact +:name=agent99'))
467
+ self.len(1, await core.nodes('risk:extortion -> risk:compromise :target -> ps:contact +:orgname=acme'))
468
+ self.len(1, await core.nodes('risk:extortion :reporter -> ou:org +:name=vertex'))
469
+
397
470
  async def test_model_risk_mitigation(self):
398
471
  async with self.getTestCore() as core:
399
472
  nodes = await core.nodes('''[
@@ -3,6 +3,8 @@ import synapse.common as s_common
3
3
  import synapse.tests.files as s_t_files
4
4
  import synapse.tests.utils as s_t_utils
5
5
 
6
+ import synapse.lib.autodoc as s_l_autodoc
7
+
6
8
  import synapse.tools.autodoc as s_autodoc
7
9
 
8
10
  class TestAutoDoc(s_t_utils.SynTest):
@@ -126,6 +128,7 @@ class TestAutoDoc(s_t_utils.SynTest):
126
128
  self.isin('StormvarServiceCell Storm Service', s)
127
129
  self.isin('This documentation is generated for version 0.0.1 of the service.', s)
128
130
  self.isin('Storm Package\\: stormvar', s)
131
+
129
132
  self.isin('.. _stormcmd-stormvar-magic:\n', s)
130
133
  self.isin('magic\n-----', s)
131
134
  self.isin('Test stormvar support', s)
@@ -136,6 +139,10 @@ class TestAutoDoc(s_t_utils.SynTest):
136
139
  self.isin('nodedata with the following keys', s)
137
140
  self.isin('``foo`` on ``inet:ipv4``', s)
138
141
 
142
+ self.isin('.. _stormmod-stormvar-apimod', s)
143
+ self.isin('status()', s)
144
+ self.notin('testmod', s)
145
+
139
146
  async def test_tools_autodoc_stormpkg(self):
140
147
 
141
148
  with self.getTestDir() as path:
@@ -153,6 +160,7 @@ class TestAutoDoc(s_t_utils.SynTest):
153
160
 
154
161
  self.isin('Storm Package\\: testpkg', s)
155
162
  self.isin('This documentation is generated for version 0.0.1 of the package.', s)
163
+
156
164
  self.isin('This package implements the following Storm Commands.', s)
157
165
  self.isin('.. _stormcmd-testpkg-testpkgcmd', s)
158
166
 
@@ -171,6 +179,23 @@ class TestAutoDoc(s_t_utils.SynTest):
171
179
  self.isin('testpkg.baz', s)
172
180
  self.isin("Help on baz opt (default: ('-7days', 'now'))", s)
173
181
 
182
+ self.isin('This package implements the following Storm Modules.', s)
183
+ self.isin('.. _stormmod-testpkg-apimod', s)
184
+
185
+ self.notin('testmod', s)
186
+
187
+ self.isin('search(text, mintime=-30days)', s)
188
+ self.isin('text (str): The text.', s)
189
+ self.isin('Yields:', s)
190
+ self.isin('The return type is ``node``.', s)
191
+
192
+ self.isin('status()', s)
193
+
194
+ # coverage for no apidefs
195
+ rst = s_l_autodoc.RstHelp()
196
+ await s_autodoc.processStormModules(rst, 'foo', [])
197
+ self.eq('\nStorm Modules\n=============\n\nThis package does not export any Storm APIs.\n', rst.getRstText())
198
+
174
199
  async def test_tools_autodoc_stormtypes(self):
175
200
  with self.getTestDir() as path:
176
201
 
@@ -69,6 +69,10 @@ class GenPkgTest(s_test.SynTest):
69
69
  ymlpath = s_common.genpath(dirname, 'files', 'stormpkg', 'badjsonpkg.yaml')
70
70
  await s_genpkg.main((ymlpath,))
71
71
 
72
+ with self.raises(s_exc.SchemaViolation):
73
+ ymlpath = s_common.genpath(dirname, 'files', 'stormpkg', 'badapidef.yaml')
74
+ await s_genpkg.main((ymlpath,))
75
+
72
76
  ymlpath = s_common.genpath(dirname, 'files', 'stormpkg', 'testpkg.yaml')
73
77
  async with self.getTestCore() as core:
74
78
 
@@ -98,10 +102,12 @@ class GenPkgTest(s_test.SynTest):
98
102
  self.eq(pdef['version'], '0.0.1')
99
103
  self.eq(pdef['modules'][0]['name'], 'testmod')
100
104
  self.eq(pdef['modules'][0]['storm'], 'inet:ipv4\n')
101
- self.eq(pdef['modules'][1]['name'], 'testpkg.testext')
102
- self.eq(pdef['modules'][1]['storm'], 'inet:fqdn\n')
103
- self.eq(pdef['modules'][2]['name'], 'testpkg.testextfile')
105
+ self.eq(pdef['modules'][1]['name'], 'apimod')
106
+ self.isin('function search', pdef['modules'][1]['storm'])
107
+ self.eq(pdef['modules'][2]['name'], 'testpkg.testext')
104
108
  self.eq(pdef['modules'][2]['storm'], 'inet:fqdn\n')
109
+ self.eq(pdef['modules'][3]['name'], 'testpkg.testextfile')
110
+ self.eq(pdef['modules'][3]['storm'], 'inet:fqdn\n')
105
111
  self.eq(pdef['commands'][0]['name'], 'testpkgcmd')
106
112
  self.eq(pdef['commands'][0]['storm'], 'inet:ipv6\n')
107
113