synapse 2.154.1__py311-none-any.whl → 2.156.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 (74) hide show
  1. synapse/cmds/cortex.py +2 -14
  2. synapse/common.py +13 -36
  3. synapse/cortex.py +15 -508
  4. synapse/lib/ast.py +215 -22
  5. synapse/lib/cell.py +35 -8
  6. synapse/lib/certdir.py +11 -0
  7. synapse/lib/cmdr.py +0 -5
  8. synapse/lib/gis.py +2 -2
  9. synapse/lib/httpapi.py +14 -43
  10. synapse/lib/layer.py +64 -201
  11. synapse/lib/lmdbslab.py +11 -0
  12. synapse/lib/node.py +1 -3
  13. synapse/lib/parser.py +10 -0
  14. synapse/lib/slabseqn.py +2 -1
  15. synapse/lib/snap.py +121 -21
  16. synapse/lib/spooled.py +9 -0
  17. synapse/lib/storm.lark +23 -6
  18. synapse/lib/storm.py +16 -339
  19. synapse/lib/storm_format.py +5 -0
  20. synapse/lib/stormhttp.py +10 -1
  21. synapse/lib/stormlib/gen.py +1 -2
  22. synapse/lib/stormlib/gis.py +41 -0
  23. synapse/lib/stormlib/graph.py +2 -1
  24. synapse/lib/stormlib/stats.py +21 -2
  25. synapse/lib/stormlib/storm.py +16 -1
  26. synapse/lib/stormtypes.py +244 -16
  27. synapse/lib/types.py +16 -2
  28. synapse/lib/version.py +2 -2
  29. synapse/lib/view.py +118 -25
  30. synapse/models/base.py +2 -2
  31. synapse/models/inet.py +60 -30
  32. synapse/models/infotech.py +130 -8
  33. synapse/models/orgs.py +3 -0
  34. synapse/models/proj.py +3 -0
  35. synapse/models/risk.py +24 -6
  36. synapse/models/syn.py +0 -38
  37. synapse/tests/test_cmds_cortex.py +1 -1
  38. synapse/tests/test_cortex.py +70 -338
  39. synapse/tests/test_lib_agenda.py +19 -54
  40. synapse/tests/test_lib_aha.py +97 -0
  41. synapse/tests/test_lib_ast.py +596 -0
  42. synapse/tests/test_lib_grammar.py +30 -10
  43. synapse/tests/test_lib_httpapi.py +33 -49
  44. synapse/tests/test_lib_layer.py +19 -234
  45. synapse/tests/test_lib_lmdbslab.py +22 -0
  46. synapse/tests/test_lib_snap.py +9 -0
  47. synapse/tests/test_lib_spooled.py +4 -0
  48. synapse/tests/test_lib_storm.py +16 -309
  49. synapse/tests/test_lib_stormlib_gis.py +21 -0
  50. synapse/tests/test_lib_stormlib_stats.py +107 -20
  51. synapse/tests/test_lib_stormlib_storm.py +25 -0
  52. synapse/tests/test_lib_stormtypes.py +253 -8
  53. synapse/tests/test_lib_types.py +40 -0
  54. synapse/tests/test_lib_view.py +6 -13
  55. synapse/tests/test_model_base.py +1 -1
  56. synapse/tests/test_model_inet.py +15 -0
  57. synapse/tests/test_model_infotech.py +110 -0
  58. synapse/tests/test_model_orgs.py +10 -0
  59. synapse/tests/test_model_person.py +0 -3
  60. synapse/tests/test_model_proj.py +2 -1
  61. synapse/tests/test_model_risk.py +24 -0
  62. synapse/tests/test_model_syn.py +20 -34
  63. synapse/tests/test_tools_csvtool.py +2 -1
  64. synapse/tests/test_tools_feed.py +4 -30
  65. synapse/tools/csvtool.py +2 -1
  66. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/METADATA +9 -9
  67. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/RECORD +70 -72
  68. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/WHEEL +1 -1
  69. synapse/cmds/cron.py +0 -726
  70. synapse/cmds/trigger.py +0 -319
  71. synapse/tests/test_cmds_cron.py +0 -453
  72. synapse/tests/test_cmds_trigger.py +0 -176
  73. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/LICENSE +0 -0
  74. {synapse-2.154.1.dist-info → synapse-2.156.0.dist-info}/top_level.txt +0 -0
@@ -656,6 +656,109 @@ class AstTest(s_test.SynTest):
656
656
  self.len(0, await core.nodes('[ inet:ipv4=1.2.3.4 ] :foo -> *'))
657
657
  self.len(0, await core.nodes('[ inet:ipv4=1.2.3.4 ] :asn -> inet:asn'))
658
658
 
659
+ async def test_ast_edge_walknjoin(self):
660
+
661
+ async with self.getTestCore() as core:
662
+
663
+ await core.nodes('[test:str=foo :hehe=bar +(foobar)> { [ test:str=baz ] }]')
664
+
665
+ nodes = await core.nodes('test:str=foo --+> *')
666
+ self.len(2, nodes)
667
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
668
+ self.eq(('test:str', 'baz'), nodes[1].ndef)
669
+
670
+ nodes = await core.nodes('test:str=baz <+-- *')
671
+ self.len(2, nodes)
672
+ self.eq(('test:str', 'baz'), nodes[0].ndef)
673
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
674
+
675
+ nodes = await core.nodes('test:str=foo -(foobar)+> *')
676
+ self.len(2, nodes)
677
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
678
+ self.eq(('test:str', 'baz'), nodes[1].ndef)
679
+
680
+ nodes = await core.nodes('test:str=baz <+(foobar)- *')
681
+ self.len(2, nodes)
682
+ self.eq(('test:str', 'baz'), nodes[0].ndef)
683
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
684
+
685
+ await core.nodes('test:str=foo [ +(coffeeone)> { [ test:str=arabica ] } ]')
686
+ await core.nodes('test:str=foo [ +(coffeetwo)> { [ test:str=robusta ] } ]')
687
+ await core.nodes('[ test:int=28 +(coffeethree)> { test:str=arabica } ]')
688
+
689
+ nodes = await core.nodes('test:str=foo -((coffeeone, coffeetwo))+> *')
690
+ self.len(3, nodes)
691
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
692
+ self.eq(('test:str', 'arabica'), nodes[1].ndef)
693
+ self.eq(('test:str', 'robusta'), nodes[2].ndef)
694
+
695
+ await core.nodes('[test:str=neato :hehe=haha +(stuff)> { [inet:ipv4=1.2.3.0/24] }]')
696
+ await core.nodes('[test:str=burrito :hehe=stuff <(stuff)+ { test:str=baz }]')
697
+ await core.nodes('test:str=neato [ <(other)+ { test:str=foo } ]')
698
+
699
+ nodes = await core.nodes('$edge=stuff test:str=neato -($edge)+> *')
700
+ self.len(257, nodes)
701
+ self.eq(('test:str', 'neato'), nodes[0].ndef)
702
+ for n in nodes[1:]:
703
+ self.eq('inet:ipv4', n.ndef[0])
704
+
705
+ nodes = await core.nodes('test:str=neato | tee { --+> * } { <+(other)- * }')
706
+ self.len(259, nodes)
707
+ self.eq(('test:str', 'neato'), nodes[0].ndef)
708
+ self.eq(('test:str', 'foo'), nodes[-1].ndef)
709
+ self.eq(('test:str', 'neato'), nodes[-2].ndef)
710
+
711
+ for n in nodes[1:257]:
712
+ self.eq('inet:ipv4', n.ndef[0])
713
+
714
+ await core.nodes('test:str=foo [ +(wat)> {[test:int=12]}]')
715
+
716
+ nodes = await core.nodes('test:str=foo -(other)+> test:str')
717
+ self.len(2, nodes)
718
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
719
+ self.eq(('test:str', 'neato'), nodes[1].ndef)
720
+
721
+ with self.raises(s_exc.BadSyntax):
722
+ await core.nodes('test:str=neato --+> test:str')
723
+
724
+ with self.raises(s_exc.BadSyntax):
725
+ await core.nodes('test:str <+-- test:str')
726
+
727
+ nodes = await core.nodes('test:str=foo -(*)+> test:str')
728
+ self.len(5, nodes)
729
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
730
+ ndefs = [n.ndef for n in nodes[1:]]
731
+ self.isin(('test:str', 'arabica'), ndefs)
732
+ self.isin(('test:str', 'robusta'), ndefs)
733
+ self.isin(('test:str', 'baz'), ndefs)
734
+ self.isin(('test:str', 'neato'), ndefs)
735
+ self.notin(('test:int', 12), ndefs)
736
+
737
+ nodes = await core.nodes('test:str=foo -(*)+> *')
738
+ self.len(6, nodes)
739
+ self.eq(('test:str', 'foo'), nodes[0].ndef)
740
+ ndefs = [n.ndef for n in nodes[1:]]
741
+ self.isin(('test:int', 12), ndefs)
742
+
743
+ nodes = await core.nodes('test:str=arabica <+(*)- test:str')
744
+ self.len(2, nodes)
745
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
746
+ self.eq(('test:str', 'foo'), nodes[1].ndef)
747
+
748
+ nodes = await core.nodes('test:str=arabica <+(*)- *')
749
+ self.len(3, nodes)
750
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
751
+ ndefs = [n.ndef for n in nodes[1:]]
752
+ self.isin(('test:str', 'foo'), ndefs)
753
+ self.isin(('test:int', 28), ndefs)
754
+
755
+ await core.nodes('test:str=arabica [ <(place)+ { [ test:str=coffeebar] } ]')
756
+ nodes = await core.nodes('test:str=arabica <+((place, coffeeone))- *')
757
+ self.len(3, nodes)
758
+ self.eq(('test:str', 'arabica'), nodes[0].ndef)
759
+ self.eq(('test:str', 'coffeebar'), nodes[1].ndef)
760
+ self.eq(('test:str', 'foo'), nodes[2].ndef)
761
+
659
762
  async def test_ast_lift_filt_array(self):
660
763
 
661
764
  async with self.getTestCore() as core:
@@ -1738,6 +1841,243 @@ class AstTest(s_test.SynTest):
1738
1841
  evnt = firs[0]
1739
1842
  self.eq(evnt[1].get('data'), {'total': 3})
1740
1843
 
1844
+ async def test_ast_emptyblock(self):
1845
+
1846
+ async with self.getTestCore() as core:
1847
+ q = '''
1848
+ empty {
1849
+ $lib.print("a fancy but empty block")
1850
+ }
1851
+ '''
1852
+ msgs = await core.stormlist(q)
1853
+ self.stormIsInPrint('a fancy but empty block', msgs)
1854
+
1855
+ q = '''
1856
+ empty {
1857
+ [test:str=neato]
1858
+ }
1859
+ [ :hehe=stuff ]
1860
+ '''
1861
+ msgs = await core.stormlist(q)
1862
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1863
+ self.len(1, nodes)
1864
+ props = nodes[0][1]['props']
1865
+ self.eq('stuff', props.get('hehe'))
1866
+
1867
+ q = '''
1868
+ empty {
1869
+ $lib.print("some empty block")
1870
+ }
1871
+ [test:str=synapse]
1872
+ '''
1873
+ msgs = await core.stormlist(q)
1874
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1875
+ self.len(1, nodes)
1876
+ self.stormIsInPrint('some empty block', msgs)
1877
+
1878
+ q = '''
1879
+ for $i in $lib.range(10) {
1880
+ if ($i > 5) {
1881
+ [test:int=$i]
1882
+ }
1883
+ } | empty { $lib.print(`count is {$i}`) }
1884
+ '''
1885
+ msgs = await core.stormlist(q)
1886
+ self.stormNotInPrint('count is', msgs)
1887
+
1888
+ q = '''
1889
+ for $i in $lib.range(10) {
1890
+ $lib.print(`count is {$i}`)
1891
+ } | empty { $lib.print(`pipeline is empty`) }
1892
+ '''
1893
+ msgs = await core.stormlist(q)
1894
+ self.stormIsInPrint('count is', msgs)
1895
+ self.stormIsInPrint('pipeline is empty', msgs)
1896
+
1897
+ q = '''
1898
+ [test:str=burrito]
1899
+ empty {
1900
+ [test:str=awesome]
1901
+ }
1902
+ '''
1903
+ msgs = await core.stormlist(q)
1904
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1905
+ self.len(1, nodes)
1906
+ self.eq(('test:str', 'burrito'), nodes[0][0])
1907
+
1908
+ q = '''
1909
+ $lib.print("OH YEA")
1910
+ empty {
1911
+ [test:str=possum]
1912
+ }
1913
+ '''
1914
+ msgs = await core.stormlist(q)
1915
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1916
+ self.len(1, nodes)
1917
+ self.eq(('test:str', 'possum'), nodes[0][0])
1918
+ self.stormIsInPrint('OH YEA', msgs)
1919
+
1920
+ q = '''
1921
+ empty {
1922
+ [test:str=foo]
1923
+ }
1924
+
1925
+ empty {
1926
+ [test:bstr=bar]
1927
+ }
1928
+ '''
1929
+ msgs = await core.stormlist(q)
1930
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1931
+ self.len(1, nodes)
1932
+ self.eq(('test:str', 'foo'), nodes[0][0])
1933
+
1934
+ q = '''
1935
+ empty {
1936
+ $lib.print('call me')
1937
+ }
1938
+
1939
+ $lib.print('ishmael')
1940
+
1941
+ empty {
1942
+ $lib.print('some years ago')
1943
+ }
1944
+
1945
+ [test:str="moby dick"]
1946
+
1947
+ empty {
1948
+ $lib.print('never mind')
1949
+ }
1950
+
1951
+ empty {
1952
+ $lib.print('how long')
1953
+ }
1954
+
1955
+ [ :hehe=haha ]
1956
+ '''
1957
+ msgs = await core.stormlist(q)
1958
+ self.stormIsInPrint('call me', msgs)
1959
+ self.stormIsInPrint('ishmael', msgs)
1960
+ self.stormIsInPrint('some years ago', msgs)
1961
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1962
+ self.len(1, nodes)
1963
+ self.eq(('test:str', 'moby dick'), nodes[0][0])
1964
+ self.eq('haha', nodes[0][1]['props']['hehe'])
1965
+ self.stormNotInPrint('never mind', msgs)
1966
+ self.stormNotInPrint('how long', msgs)
1967
+
1968
+ q = '''
1969
+ function foo(x) {
1970
+ empty {
1971
+ $lib.print($x)
1972
+ }
1973
+
1974
+ return()
1975
+ }
1976
+
1977
+ [test:str=biz :hehe=baz]
1978
+ $foo(:hehe)
1979
+ '''
1980
+ msgs = await core.stormlist(q)
1981
+ self.stormIsInPrint("baz", msgs)
1982
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1983
+ self.len(1, nodes)
1984
+ self.eq(('test:str', 'biz'), nodes[0][0])
1985
+
1986
+ q = '''
1987
+ [test:str=coffee :hehe=pourover] $beep=:hehe | spin | empty { $lib.print("blorp") }
1988
+ '''
1989
+ msgs = await core.stormlist(q)
1990
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1991
+ self.len(0, nodes)
1992
+ self.stormIsInPrint('blorp', msgs)
1993
+
1994
+ q = '''
1995
+ [test:str=latte :hehe=milk] $beep=:hehe | spin | empty { $lib.print($beep) }
1996
+ '''
1997
+ msgs = await core.stormlist(q)
1998
+ nodes = [m[1] for m in msgs if m[0] == 'node']
1999
+ self.len(0, nodes)
2000
+ self.stormIsInErr('Empty block query must be runtsafe', msgs)
2001
+
2002
+ q = '''
2003
+ function foo() {
2004
+ for $x in $lib.range(10) {
2005
+ emit $x
2006
+ }
2007
+ }
2008
+
2009
+ for $data in $foo() {
2010
+ if ($data > 10000) {
2011
+ [test:int=$data]
2012
+ }
2013
+ }
2014
+
2015
+ empty {
2016
+ [test:int=1000]
2017
+ }
2018
+ '''
2019
+ msgs = await core.stormlist(q)
2020
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2021
+ self.len(1, nodes)
2022
+ self.eq(('test:int', 1000), nodes[0][0])
2023
+
2024
+ q = '''
2025
+ empty {
2026
+ [test:int=12345]
2027
+ }
2028
+ '''
2029
+ idens = [nodes[0][1]['iden'],]
2030
+ msgs = await core.stormlist(q, opts={'idens': idens})
2031
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2032
+ self.len(1, nodes)
2033
+ self.eq(('test:int', 1000), nodes[0][0])
2034
+
2035
+ q = '''
2036
+ function foo() {
2037
+ empty {
2038
+ $lib.print('foobarbaz')
2039
+ }
2040
+ [test:int=12]
2041
+ }
2042
+
2043
+ yield $foo()
2044
+ empty {
2045
+ $lib.print('neato')
2046
+ }
2047
+ '''
2048
+ msgs = await core.stormlist(q)
2049
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2050
+ self.len(1, nodes)
2051
+ self.eq(('test:int', 12), nodes[0][0])
2052
+ self.stormIsInPrint('foobarbaz', msgs)
2053
+ self.stormNotInPrint('neato', msgs)
2054
+
2055
+ q = '''
2056
+ function foo() {
2057
+ for $x in $lib.range(2) {
2058
+ emit $x
2059
+ empty {
2060
+ $lib.print(`count is {$x}`)
2061
+ }
2062
+ }
2063
+ }
2064
+ for $x in $foo() {
2065
+ [test:int=$x]
2066
+ }
2067
+ '''
2068
+ msgs = await core.stormlist(q)
2069
+ order = [m[0] for m in msgs]
2070
+ nodes = [m[1] for m in msgs if m[0] == 'node']
2071
+ ndefs = [n[0] for n in nodes]
2072
+
2073
+ self.len(2, nodes)
2074
+ self.eq(order, ['init', 'node:edits', 'node', 'print', 'node:edits', 'node', 'print', 'fini'])
2075
+ self.isin(('test:int', 0), ndefs)
2076
+ self.isin(('test:int', 1), ndefs)
2077
+
2078
+ self.stormIsInPrint('count is 0', msgs)
2079
+ self.stormIsInPrint('count is 1', msgs)
2080
+
1741
2081
  async def test_ast_cmdargs(self):
1742
2082
 
1743
2083
  async with self.getTestCore() as core:
@@ -2527,6 +2867,166 @@ class AstTest(s_test.SynTest):
2527
2867
  # one for the refs edge (via doedges) and one for the rule..
2528
2868
  self.len(2, nodes[1][1]['path']['edges'])
2529
2869
 
2870
+ async def test_ast_subgraph_caching(self):
2871
+ async with self.getTestCore() as core:
2872
+ limits = (0, 1, 10, 255, 256, 10000)
2873
+ ipv4s = await core.nodes('[inet:ipv4=1.2.3.0/24]')
2874
+ neato = await core.nodes('''[
2875
+ test:str=neato +(refs)> { inet:ipv4 }
2876
+ ]''')
2877
+ await core.nodes('[test:str=neato +(selfrefs)> { test:str=neato }]')
2878
+ self.len(1, neato)
2879
+
2880
+ iden = neato[0].iden()
2881
+ idens = [iden,]
2882
+ opts = {
2883
+ 'graph': {
2884
+ 'degrees': None,
2885
+ 'edges': True,
2886
+ 'refs': True,
2887
+ 'existing': idens
2888
+ },
2889
+ 'idens': idens
2890
+ }
2891
+
2892
+ def testedges(msgs):
2893
+ self.len(259, msgs)
2894
+ for m in msgs[:-2]:
2895
+ if m[0] != 'node':
2896
+ continue
2897
+ node = m[1]
2898
+ edges = node[1]['path']['edges']
2899
+ self.len(1, edges)
2900
+ edgeiden, edgedata = edges[0]
2901
+ self.eq(edgeiden, iden)
2902
+ self.true(edgedata.get('reverse', False))
2903
+ self.eq(edgedata['verb'], 'refs')
2904
+ self.eq(edgedata['type'], 'edge')
2905
+ selfref = msgs[-2]
2906
+ node = selfref[1]
2907
+ edges = node[1]['path']['edges']
2908
+ self.len(258, edges)
2909
+
2910
+ for limit in limits:
2911
+ opts['graph']['edgelimit'] = limit
2912
+ msgs = await core.stormlist('tee { --> * } { <-- * }', opts=opts)
2913
+ testedges(msgs)
2914
+
2915
+ burrito = await core.nodes('[test:str=burrito <(awesome)+ { inet:ipv4 }]')
2916
+ self.len(1, burrito)
2917
+
2918
+ iden = burrito[0].iden()
2919
+ for m in msgs:
2920
+ if m[0] != 'node':
2921
+ continue
2922
+ node = m[1]
2923
+ idens.append(node[1]['iden'])
2924
+
2925
+ opts['graph']['existing'] = idens
2926
+ opts['idens'] = [ipv4s[0].iden(),]
2927
+ ipidens = [n.iden() for n in ipv4s]
2928
+ ipidens.append(neato[0].iden())
2929
+ for limit in limits:
2930
+ opts['graph']['edgelimit'] = limit
2931
+ msgs = await core.stormlist('tee { --> * } { <-- * }', opts=opts)
2932
+ self.len(4, msgs)
2933
+
2934
+ node = msgs[1][1]
2935
+ self.eq(node[0], ('test:str', 'burrito'))
2936
+ edges = node[1]['path']['edges']
2937
+ self.len(256, edges)
2938
+
2939
+ for edge in edges:
2940
+ edgeiden, edgedata = edge
2941
+ self.isin(edgeiden, ipidens)
2942
+ self.true(edgedata.get('reverse', False))
2943
+ self.eq(edgedata['verb'], 'awesome')
2944
+ self.eq(edgedata['type'], 'edge')
2945
+
2946
+ node = msgs[2][1]
2947
+ self.eq(node[0], ('test:str', 'neato'))
2948
+ self.len(256, edges)
2949
+ edges = node[1]['path']['edges']
2950
+ for edge in edges:
2951
+ edgeiden, edgedata = edge
2952
+ self.isin(edgeiden, ipidens)
2953
+ self.eq(edgedata['type'], 'edge')
2954
+ if edgedata['verb'] == 'selfrefs':
2955
+ self.eq(edgeiden, neato[0].iden())
2956
+ else:
2957
+ self.eq(edgedata['verb'], 'refs')
2958
+ self.false(edgedata.get('reverse', False))
2959
+
2960
+ opts['graph'].pop('existing', None)
2961
+ opts['idens'] = [neato[0].iden(),]
2962
+ for limit in limits:
2963
+ opts['graph']['edgelimit'] = limit
2964
+ msgs = await core.stormlist('tee { --> * } { <-- * }', opts=opts)
2965
+ selfrefs = 0
2966
+ for m in msgs:
2967
+ if m[0] != 'node':
2968
+ continue
2969
+
2970
+ node = m[1]
2971
+ form = node[0][0]
2972
+ edges = node[1]['path'].get('edges', ())
2973
+ if form == 'inet:ipv4':
2974
+ self.len(0, edges)
2975
+ elif form == 'test:str':
2976
+ self.len(258, edges)
2977
+ for e in edges:
2978
+ self.isin(e[0], ipidens)
2979
+ self.eq('edge', e[1]['type'])
2980
+ if e[0] == neato[0].iden():
2981
+ selfrefs += 1
2982
+ self.eq('selfrefs', e[1]['verb'])
2983
+ else:
2984
+ self.eq('refs', e[1]['verb'])
2985
+ self.eq(selfrefs, 2)
2986
+
2987
+ boop = await core.nodes('[test:str=boop +(refs)> {[inet:ipv4=5.6.7.0/24]}]')
2988
+ await core.nodes('[test:str=boop <(refs)+ {[inet:ipv4=4.5.6.0/24]}]')
2989
+ self.len(1, boop)
2990
+ boopiden = boop[0].iden()
2991
+ opts['idens'] = [boopiden,]
2992
+ for limit in limits:
2993
+ opts['graph']['edgelimit'] = limit
2994
+ msgs = await core.stormlist('tee --join { --> * } { <-- * }', opts=opts)
2995
+ self.len(515, msgs)
2996
+
2997
+ async def test_ast_subgraph_existing_prop_edges(self):
2998
+
2999
+ async with self.getTestCore() as core:
3000
+ (fn,) = await core.nodes('[ file:bytes=(woot,) :md5=e5a23e8a2c0f98850b1a43b595c08e63 ]')
3001
+ fiden = fn.iden()
3002
+
3003
+ rules = {
3004
+ 'degrees': None,
3005
+ 'edges': True,
3006
+ 'refs': True,
3007
+ 'existing': [fiden]
3008
+ }
3009
+
3010
+ nodes = []
3011
+
3012
+ async with await core.snap() as snap:
3013
+ async for node, path in snap.storm(':md5 -> hash:md5', opts={'idens': [fiden], 'graph': rules}):
3014
+ nodes.append(node)
3015
+
3016
+ edges = path.metadata.get('edges')
3017
+ self.len(1, edges)
3018
+ self.eq(edges, [
3019
+ [fn.iden(), {
3020
+ "type": "prop",
3021
+ "prop": "md5",
3022
+ "reverse": True
3023
+ }]
3024
+ ])
3025
+
3026
+ self.true(path.metadata.get('graph:seed'))
3027
+
3028
+ self.len(1, nodes)
3029
+
2530
3030
  async def test_ast_double_init_fini(self):
2531
3031
  async with self.getTestCore() as core:
2532
3032
  q = '''
@@ -2618,8 +3118,11 @@ class AstTest(s_test.SynTest):
2618
3118
  self.len(2, await core.nodes('test:str +#taga*:score'))
2619
3119
  self.len(1, await core.nodes('test:str +#tagaa:score=5'))
2620
3120
  self.len(1, await core.nodes('test:str +#tagaa:score<(2+4)'))
3121
+ self.len(0, await core.nodes('test:str +#tagaa:score<-5'))
2621
3122
  self.len(1, await core.nodes('test:str +#tagaa:score*range=(4,6)'))
3123
+ self.len(0, await core.nodes('test:str +#taga*:score <- *'))
2622
3124
  self.len(1, await core.nodes('test:str +#taga*:score <(*)- *'))
3125
+ self.len(3, await core.nodes('test:str +#taga*:score <+(*)- *'))
2623
3126
  self.len(2, await core.nodes('$tag=taga* test:str +#$tag:score'))
2624
3127
  self.len(1, await core.nodes('$tag=tagaa test:str +#$tag:score=5'))
2625
3128
  self.len(1, await core.nodes('$tag=tagaa test:str +#$tag:score*range=(4,6)'))
@@ -2656,3 +3159,96 @@ class AstTest(s_test.SynTest):
2656
3159
 
2657
3160
  with self.raises(s_exc.BadSyntax):
2658
3161
  await core.nodes('$tag=taga test:str +#foo.$"tag".$"tag".*:score=2023')
3162
+
3163
+ async def test_ast_righthand_relprop(self):
3164
+ async with self.getTestCore() as core:
3165
+ await core.nodes('''[
3166
+ (test:type10=one :intprop=21 :int2=21)
3167
+ (test:type10=two :intprop=21 :int2=29)
3168
+ (test:float=13.4 :closed=14.0 :open=14.0)
3169
+ (test:float=14.5 :closed=12.0 :open=13.0)
3170
+ (test:float=15.6 :closed=12.0)
3171
+ (test:float=16.7)
3172
+ ]''')
3173
+
3174
+ nodes = await core.nodes('test:type10 +(:intprop = :int2)')
3175
+ self.len(1, nodes)
3176
+ self.eq(nodes[0].ndef, ('test:type10', 'one'))
3177
+
3178
+ nodes = await core.nodes('test:float +(:closed = 12.0 and :open)')
3179
+ self.len(1, nodes)
3180
+ self.eq(nodes[0].ndef, ('test:float', 14.5))
3181
+
3182
+ nodes = await core.nodes('test:float +(:open = $lib.null)')
3183
+ self.len(0, nodes)
3184
+
3185
+ nodes = await core.nodes('test:float +(:closed = :open)')
3186
+ self.len(1, nodes)
3187
+ self.eq(nodes[0].ndef, ('test:float', 13.4))
3188
+
3189
+ nodes = await core.nodes('test:float $foobar=:open +(:closed = $foobar)')
3190
+ self.len(1, nodes)
3191
+ self.eq(nodes[0].ndef, ('test:float', 13.4))
3192
+
3193
+ nodes = await core.nodes('test:type10 $foobar=:int2 +(:intprop = $foobar)')
3194
+ self.len(1, nodes)
3195
+ self.eq(nodes[0].ndef, ('test:type10', 'one'))
3196
+
3197
+ async def test_ast_propvalue(self):
3198
+ async with self.getTestCore() as core:
3199
+
3200
+ # Create node with data prop, assign data prop to var, update var
3201
+ q = '[ it:exec:query=(test1,) :opts=({"foo": "bar"}) ] $opts=:opts $opts.bar = "baz"'
3202
+ nodes = await core.nodes(q)
3203
+ self.len(1, nodes)
3204
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar'})
3205
+
3206
+ q = '[ it:exec:query=(test1,) :opts=({"foo": "bar"}) ] $opts=:opts $opts.bar = "baz" [ :opts=$opts ]'
3207
+ nodes = await core.nodes(q)
3208
+ self.len(1, nodes)
3209
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar', 'bar': 'baz'})
3210
+
3211
+ q = '''
3212
+ '''
3213
+ msgs = await core.stormlist('[ it:exec:query=(test2,) :opts=({"foo": "bar"}) ]')
3214
+ self.stormHasNoWarnErr(msgs)
3215
+
3216
+ # Lift node with data prop, assign data prop to var, update var
3217
+ q = 'it:exec:query=(test2,) $opts=:opts $opts.bar = "baz"'
3218
+ nodes = await core.nodes(q)
3219
+ self.len(1, nodes)
3220
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar'})
3221
+
3222
+ q = 'it:exec:query=(test2,) $opts=:opts $opts.bar = "baz" [ :opts=$opts ]'
3223
+ nodes = await core.nodes(q)
3224
+ self.len(1, nodes)
3225
+ self.eq(nodes[0].props.get('opts'), {'foo': 'bar', 'bar': 'baz'})
3226
+
3227
+ # Create node for the lift below
3228
+ q = '''
3229
+ [ it:app:snort:hit=*
3230
+ :flow={[ inet:flow=* :raw=({"foo": "bar"}) ]}
3231
+ ]
3232
+ '''
3233
+ nodes = await core.nodes(q)
3234
+ self.len(1, nodes)
3235
+
3236
+ # Lift node, get prop via implicit pivot, assign data prop to var, update var
3237
+ q = f'it:app:snort:hit $raw = :flow::raw $raw.baz="box" | spin | inet:flow'
3238
+ nodes = await core.nodes(q)
3239
+ self.len(1, nodes)
3240
+ self.eq(nodes[0].props.get('raw'), {'foo': 'bar'})
3241
+
3242
+ q = f'it:app:snort:hit $raw = :flow::raw $raw.baz="box" | spin | inet:flow [ :raw=$raw ]'
3243
+ nodes = await core.nodes(q)
3244
+ self.len(1, nodes)
3245
+ self.eq(nodes[0].props.get('raw'), {'foo': 'bar', 'baz': 'box'})
3246
+
3247
+ async def test_ast_subq_runtsafety(self):
3248
+
3249
+ async with self.getTestCore() as core:
3250
+ msgs = await core.stormlist('$foo={[test:str=foo] return($node.value())} $lib.print($foo)')
3251
+ self.stormIsInPrint('foo', msgs)
3252
+
3253
+ msgs = await core.stormlist('$lib.print({[test:str=foo] return($node.value())})')
3254
+ self.stormIsInPrint('foo', msgs)