librex 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. data/README.markdown +1 -1
  2. data/Rakefile +1 -1
  3. data/lib/rex/io/stream.rb +1 -1
  4. data/lib/rex/parser/nmap_xml.rb +4 -1
  5. data/lib/rex/post/meterpreter/extensions/stdapi/railgun.rb.ts.rb +6 -0
  6. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb.ut.rb +31 -0
  7. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/buffer_item.rb +47 -0
  8. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/buffer_item.rb.ut.rb +36 -0
  9. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/{model.rb → dll.rb} +4 -226
  10. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_function.rb +100 -0
  11. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_function.rb.ut.rb +42 -0
  12. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_helper.rb +148 -0
  13. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_helper.rb.ut.rb +127 -0
  14. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +2 -1
  15. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +3 -2
  16. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +1 -1
  17. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager.rb +75 -0
  18. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager.rb.ut.rb +103 -0
  19. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +44 -0
  20. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +2 -2
  21. data/lib/rex/proto/dhcp/server.rb +7 -6
  22. data/lib/rex/proto/ntlm/utils.rb +505 -100
  23. data/lib/rex/proto/rfb/cipher.rb +6 -0
  24. data/lib/rex/proto/smb/client.rb +40 -332
  25. data/lib/rex/proto/smb/simpleclient.rb +3 -1
  26. data/lib/rex/proto/smb/utils.rb +0 -4
  27. metadata +14 -4
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
4
+
5
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_function'
6
+ require 'test/unit'
7
+
8
+ module Rex
9
+ module Post
10
+ module Meterpreter
11
+ module Extensions
12
+ module Stdapi
13
+ module Railgun
14
+ class DLLFunction::UnitTest < Test::Unit::TestCase
15
+
16
+ VALID_RETURN_TYPE = 'DWORD'
17
+ NON_RETURN_DATATYPE = 'INVALID_RETURN_TYPE'
18
+
19
+ VALID_DIRECTION = 'out'
20
+ UNKNOWN_DIRECTION = 'unknown'
21
+
22
+ VALID_DATATYPE = 'PBLOB'
23
+ UNKNOWN_DATATYPE = 'UNKNOWN_DATATYPE'
24
+
25
+ def test_initialize
26
+ # TODO: haven't gotten around to writing this yet. Feel free to
27
+ # skip("incomplete test coverage")
28
+ #
29
+ # assert_nothing_raised("valid initialization should not raise") do
30
+ # end
31
+ #
32
+ # assert_raised(ArgumentError, "check_type_exists should raise ArgumentError on unknown datatypes") do
33
+ # end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,148 @@
1
+ # Copyright (c) 2010, patrickHVE@googlemail.com
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ # * Redistributions of source code must retain the above copyright
7
+ # notice, this list of conditions and the following disclaimer.
8
+ # * Redistributions in binary form must reproduce the above copyright
9
+ # notice, this list of conditions and the following disclaimer in the
10
+ # documentation and/or other materials provided with the distribution.
11
+ # * The names of the author may not be used to endorse or promote products
12
+ # derived from this software without specific prior written permission.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ # DISCLAIMED. IN NO EVENT SHALL patrickHVE@googlemail.com BE LIABLE FOR ANY
18
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ module Rex
26
+ module Post
27
+ module Meterpreter
28
+ module Extensions
29
+ module Stdapi
30
+ module Railgun
31
+
32
+ #
33
+ # shared functions
34
+ #
35
+ #
36
+ module DLLHelper
37
+
38
+ # converts ruby string to zero-terminated ASCII string
39
+ def str_to_ascii_z(str)
40
+ return str+"\x00"
41
+ end
42
+
43
+ # converts 0-terminated ASCII string to ruby string
44
+ def asciiz_to_str(asciiz)
45
+ zero_byte_idx = asciiz.index("\x00")
46
+ if zero_byte_idx != nil
47
+ return asciiz[0, zero_byte_idx]
48
+ else
49
+ return asciiz
50
+ end
51
+ end
52
+
53
+ # converts ruby string to zero-terminated WCHAR string
54
+ def str_to_uni_z(str)
55
+ enc = str.unpack("C*").pack("v*")
56
+ enc += "\x00\x00"
57
+ return enc
58
+ end
59
+
60
+ # converts 0-terminated UTF16 to ruby string
61
+ def uniz_to_str(uniz)
62
+ uniz.unpack("v*").pack("C*").unpack("A*")[0]
63
+ end
64
+
65
+ # parses a number param and returns the value
66
+ # raises an exception if the param cannot be converted to a number
67
+ # examples:
68
+ # nil => 0
69
+ # 3 => 3
70
+ # "MB_OK" => 0
71
+ # "SOME_CONSTANT | OTHER_CONSTANT" => 17
72
+ # "tuna" => !!!!!!!!!!Exception
73
+ #
74
+ # Parameter "win_consts" is a WinConstantManager
75
+ def param_to_number(v, win_consts = @win_consts)
76
+ if v.class == NilClass then
77
+ return 0
78
+ elsif v.class == Fixnum then
79
+ return v # ok, it's already a number
80
+ elsif v.class == Bignum then
81
+ return v # ok, it's already a number
82
+ elsif v.class == String then
83
+ dw = win_consts.parse(v) # might raise an exception
84
+ if dw != nil
85
+ return dw
86
+ else
87
+ raise ArgumentError, "Param #{v} (class #{v.class}) cannot be converted to a number. It's a string but matches no constants I know."
88
+ end
89
+ else
90
+ raise "Param #{v} (class #{v.class}) should be a number but isn't"
91
+ end
92
+ end
93
+
94
+ # assembles the buffers "in" and "inout"
95
+ def assemble_buffer(direction, function, args)
96
+ layout = {} # paramName => BufferItem
97
+ blob = ""
98
+ #puts " building buffer: #{direction}"
99
+ function.params.each_with_index do |param_desc, param_idx|
100
+ #puts " processing #{param_desc[0]} #{param_desc[1]} #{param_desc[2]}"
101
+ # we care only about inout buffers
102
+ if param_desc[2] == direction
103
+ buffer = nil
104
+ # Special case:
105
+ # The user can choose to supply a Null pointer instead of a buffer
106
+ # in this case we don't need space in any heap buffer
107
+ if param_desc[0][0,1] == 'P' # type is a pointer
108
+ if args[param_idx] == nil
109
+ next
110
+ end
111
+ end
112
+
113
+ case param_desc[0] # required argument type
114
+ when "PDWORD"
115
+ dw = param_to_number(args[param_idx])
116
+ buffer = [dw].pack('V')
117
+ when "PWCHAR"
118
+ raise "param #{param_desc[1]}: string expected" unless args[param_idx].class == String
119
+ buffer = str_to_uni_z(args[param_idx])
120
+ when "PCHAR"
121
+ raise "param #{param_desc[1]}: string expected" unless args[param_idx].class == String
122
+ buffer = str_to_ascii_z(args[param_idx])
123
+ when "PBLOB"
124
+ raise "param #{param_desc[1]}: please supply your BLOB as string!" unless args[param_idx].class == String
125
+ buffer = args[param_idx]
126
+ # other types (non-pointers) don't reference buffers
127
+ # and don't need any treatment here
128
+ end
129
+
130
+ if buffer != nil
131
+ #puts " adding #{buffer.length} bytes to heap blob"
132
+ layout[param_desc[1]] = BufferItem.new(param_idx, blob.length, buffer.length, param_desc[0])
133
+ blob += buffer
134
+ # sf: force 8 byte alignment to satisfy x64, wont matter on x86.
135
+ while( blob.length % 8 != 0 )
136
+ blob += "\x00"
137
+ end
138
+ #puts " heap blob size now #{blob.length}"
139
+ end
140
+ end
141
+ end
142
+ #puts " built buffer: #{direction}"
143
+ return [layout, blob]
144
+ end
145
+
146
+ end
147
+
148
+ end; end; end; end; end; end;
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
4
+
5
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_helper'
6
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
7
+ require 'rex/text'
8
+ require 'test/unit'
9
+
10
+ module Rex
11
+ module Post
12
+ module Meterpreter
13
+ module Extensions
14
+ module Stdapi
15
+ module Railgun
16
+ class DLLHelper::UnitTest < Test::Unit::TestCase
17
+
18
+ ###
19
+ # We will test against this instance of DLLHelper (a module)
20
+ #
21
+ # We freeze the instance and make the reference constant to ensure consistency
22
+ ##
23
+ TEST_DLL_HELPER = Object.new.extend(DLLHelper).freeze
24
+
25
+ def test_str_to_ascii_z
26
+ original_string = '23 Skidoo!'
27
+
28
+ # converts ruby string to zero-terminated ASCII string
29
+ zero_terminated_ascii_attempt = TEST_DLL_HELPER.str_to_ascii_z(original_string)
30
+
31
+ assert(zero_terminated_ascii_attempt.end_with?("\x00"),
32
+ "str_to_ascii_z should result in a 0 terminated string")
33
+
34
+ assert(zero_terminated_ascii_attempt.start_with?(original_string),
35
+ "str_to_ascii_z should still start with original string")
36
+
37
+ assert_equal(original_string.length + 1, zero_terminated_ascii_attempt.length,
38
+ "str_to_ascii_z should have length of original pluss room for a terminal 0")
39
+ end
40
+
41
+ def test_asciiz_to_str
42
+ target_string = '23 Skidoo!'
43
+ post_zero_noise = 'Loud noises!'
44
+ zero_terminated_string = target_string + "\x00" + post_zero_noise
45
+
46
+ actual_string = TEST_DLL_HELPER.asciiz_to_str(zero_terminated_string)
47
+
48
+ assert(actual_string.start_with?(target_string),
49
+ "asciiz_to_str should preserve string before zero")
50
+
51
+ assert(!actual_string.end_with?(post_zero_noise),
52
+ "asciiz_to_str should ignore characters after zero")
53
+
54
+ assert_equal(target_string, actual_string,
55
+ "asciiz_to_str should only return the contents of the string before (exclusive) the zero")
56
+
57
+ assert_equal(target_string, TEST_DLL_HELPER.asciiz_to_str(target_string),
58
+ "asciiz_to_str should return input verbatim should that input not be zero-terminated")
59
+
60
+ end
61
+
62
+ def test_str_to_uni_z
63
+ ruby_string = "If I were a rich man..."
64
+
65
+ target_zero_terminated_unicode = Rex::Text.to_unicode(ruby_string) + "\x00\x00"
66
+ actual_zero_terminated_unicode = TEST_DLL_HELPER.str_to_uni_z(ruby_string)
67
+
68
+ assert(actual_zero_terminated_unicode.end_with?("\x00\x00"),
69
+ "str_to_uni_z should result in a double-zero terminated string")
70
+
71
+ assert_equal(target_zero_terminated_unicode, actual_zero_terminated_unicode,
72
+ "str_to_uni_z should convert ruby string to zero-terminated WCHAR string")
73
+ end
74
+
75
+ def test_uniz_to_str
76
+ target_string = 'Foo bar baz'
77
+
78
+ zero_terminated_unicode = Rex::Text.to_unicode(target_string) + "\x00\x00"
79
+
80
+ assert_equal(target_string, TEST_DLL_HELPER.uniz_to_str(zero_terminated_unicode),
81
+ 'uniz_to_str should convert 0-terminated UTF16 to ruby string')
82
+
83
+ end
84
+
85
+ def test_assemble_buffer
86
+ # TODO: provide test coverage
87
+ #skip("Currently DLLHelper.assemble_buffer does not have coverage")
88
+ end
89
+
90
+ def test_param_to_number
91
+ consts_manager = WinConstManager.new
92
+
93
+ x_key = 'X'
94
+ x_value = 23
95
+
96
+ y_key = 'Y'
97
+ y_value = 5
98
+
99
+ logical_or = x_key + '|' + y_key
100
+ target_result_of_logical_or = x_value | y_value
101
+
102
+ consts_manager.add_const(y_key, y_value)
103
+ consts_manager.add_const(x_key, x_value)
104
+
105
+ assert_equal(x_value, TEST_DLL_HELPER.param_to_number(x_key, consts_manager),
106
+ "param_to_number should return the appropriate value for a given constant")
107
+
108
+ assert_equal(y_value, TEST_DLL_HELPER.param_to_number(y_key, consts_manager),
109
+ "param_to_number should return the appropriate value for a given constant")
110
+
111
+ assert_equal(0, TEST_DLL_HELPER.param_to_number(nil, consts_manager),
112
+ "param_to_number should return zero when given nil")
113
+
114
+ assert_equal(target_result_of_logical_or, TEST_DLL_HELPER.param_to_number(logical_or, consts_manager),
115
+ "param_to_number should perform an OR should the input be in the form '#{logical_or}'")
116
+
117
+ assert_raise(ArgumentError, 'param_to_number should raise an error when a given key does not exist') do
118
+ TEST_DLL_HELPER.param_to_number('DOESNT_EXIST', consts_manager)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -26,7 +26,8 @@ require 'pp'
26
26
  require 'enumerator'
27
27
  require 'rex/post/meterpreter/extensions/stdapi/railgun/api_constants'
28
28
  require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'
29
- require 'rex/post/meterpreter/extensions/stdapi/railgun/model'
29
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_helper'
30
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/buffer_item'
30
31
 
31
32
  module Rex
32
33
  module Post
@@ -28,11 +28,13 @@
28
28
 
29
29
  require 'pp'
30
30
  require 'enumerator'
31
+
31
32
  require 'rex/post/meterpreter/extensions/stdapi/railgun/api_constants'
32
33
  require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'
33
34
  require 'rex/post/meterpreter/extensions/stdapi/railgun/util'
34
- require 'rex/post/meterpreter/extensions/stdapi/railgun/model'
35
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
35
36
  require 'rex/post/meterpreter/extensions/stdapi/railgun/multicall'
37
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll'
36
38
 
37
39
  module Rex
38
40
  module Post
@@ -45,7 +47,6 @@ module Railgun
45
47
  # The Railgun class to dynamically expose the Windows API.
46
48
  #
47
49
  class Railgun
48
-
49
50
  def initialize( client )
50
51
 
51
52
  @client = client
@@ -1,4 +1,4 @@
1
- require 'rex/post/meterpreter/extensions/stdapi/railgun/model'
1
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_helper'
2
2
 
3
3
  module Rex
4
4
  module Post
@@ -0,0 +1,75 @@
1
+ # Copyright (c) 2010, patrickHVE@googlemail.com
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ # * Redistributions of source code must retain the above copyright
7
+ # notice, this list of conditions and the following disclaimer.
8
+ # * Redistributions in binary form must reproduce the above copyright
9
+ # notice, this list of conditions and the following disclaimer in the
10
+ # documentation and/or other materials provided with the distribution.
11
+ # * The names of the author may not be used to endorse or promote products
12
+ # derived from this software without specific prior written permission.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ # DISCLAIMED. IN NO EVENT SHALL patrickHVE@googlemail.com BE LIABLE FOR ANY
18
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+
25
+ module Rex
26
+ module Post
27
+ module Meterpreter
28
+ module Extensions
29
+ module Stdapi
30
+ module Railgun
31
+
32
+ #
33
+ # Manages our library of windows constants
34
+ #
35
+ class WinConstManager
36
+
37
+ def initialize(initial_consts = {})
38
+ @consts = {}
39
+
40
+ initial_consts.each_pair do |name, value|
41
+ add_const(name, value)
42
+ end
43
+
44
+ # Load utility
45
+ end
46
+
47
+ def add_const(name, value)
48
+ @consts[name] = value
49
+ end
50
+
51
+ # parses a string constaining constants and returns an integer
52
+ # the string can be either "CONST" or "CONST1 | CONST2"
53
+ #
54
+ # this function will NOT throw an exception but return "nil" if it can't parse a string
55
+ def parse(s)
56
+ if s.class != String
57
+ return nil # it's not even a string'
58
+ end
59
+ return_value = 0
60
+ for one_const in s.split('|')
61
+ one_const = one_const.strip()
62
+ if not @consts.has_key? one_const
63
+ return nil # at least one "Constant" is unknown to us
64
+ end
65
+ return_value |= @consts[one_const]
66
+ end
67
+ return return_value
68
+ end
69
+
70
+ def is_parseable(s)
71
+ return parse(s) != nil
72
+ end
73
+ end
74
+
75
+ end; end; end; end; end; end
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
4
+
5
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
6
+ require 'test/unit'
7
+
8
+ module Rex
9
+ module Post
10
+ module Meterpreter
11
+ module Extensions
12
+ module Stdapi
13
+ module Railgun
14
+ class WinConstManager::UnitTest < Test::Unit::TestCase
15
+ def test_is_parseable
16
+ const_manager = WinConstManager.new
17
+
18
+ first_key = 'SOME_NUMBER'
19
+ second_key = 'SOME_OTHER_NUMBER'
20
+ boolean_logic = first_key + ' | ' + second_key
21
+
22
+ # XXX: Should check (un)parseability before adding constants too?
23
+
24
+ const_manager.add_const(first_key, 43123)
25
+ const_manager.add_const(second_key, 234)
26
+
27
+ assert(const_manager.is_parseable(boolean_logic),
28
+ "is_parseable should consider boolean logic statements parseable")
29
+
30
+ assert(const_manager.is_parseable(first_key),
31
+ "is_parseable should consider constants parseable")
32
+
33
+ assert(! const_manager.is_parseable(5),
34
+ "is_parseable should not consider non-string keys as parseable")
35
+
36
+ assert(! const_manager.is_parseable('| FOO |'),
37
+ "is_parseable should not consider malformed boolean expressions parseable")
38
+ end
39
+
40
+ def test_add_const
41
+ target_key = 'VALID_KEY'
42
+ target_value = 23
43
+
44
+ const_manager = WinConstManager.new
45
+
46
+ const_manager.add_const(target_key, target_value)
47
+
48
+ assert_equal(target_value, const_manager.parse(target_key),
49
+ "add_const should add a constant/value pair that can be trieved with parse")
50
+
51
+ end
52
+
53
+ def test_initialization
54
+ target_key = 'VALID_KEY'
55
+ target_value = 23
56
+
57
+ const_manager = WinConstManager.new(target_key => target_value)
58
+
59
+ assert_equal(target_value, const_manager.parse(target_key),
60
+ "upon initialization, should add any provided constants.")
61
+ end
62
+
63
+ def test_parse
64
+ target_key = 'VALID_KEY'
65
+ target_value = 23
66
+ invalid_key = 8
67
+
68
+ const_manager = WinConstManager.new
69
+
70
+ const_manager.add_const(target_key, target_value)
71
+
72
+ assert_equal(target_value, const_manager.parse(target_key),
73
+ "parse should retrieve the corresponding value when a key is provided")
74
+
75
+ # From API: "should not throw an exception given an invalid key"
76
+ assert_nothing_thrown do
77
+ const_manager.parse(invalid_key)
78
+ end
79
+
80
+ assert_equal(nil, const_manager.parse(invalid_key),
81
+ "parse should return nil when an invalid key is provided")
82
+
83
+ x_key = 'X'
84
+ x_value = 228
85
+ y_key = 'Y'
86
+ y_value = 15
87
+
88
+ boolean_logic = x_key + ' | ' + y_key
89
+ target_boolean_logic_result = x_value | y_value
90
+
91
+ const_manager.add_const(x_key, x_value)
92
+ const_manager.add_const(y_key, y_value)
93
+
94
+ assert_equal(target_boolean_logic_result, const_manager.parse(boolean_logic),
95
+ "parse should evaluate boolean expressions consisting of OR")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end