beaker-windows 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default do
6
+ system 'rake --tasks'
7
+ end
8
+
9
+ desc "Run spec tests"
10
+ RSpec::Core::RakeTask.new(:test) do |t|
11
+ t.rspec_opts = ['--color']
12
+ t.pattern = 'spec/'
13
+ end
@@ -0,0 +1,12 @@
1
+ %w( path powershell registry windows_feature).each do |lib|
2
+ require "beaker-windows/#{lib}"
3
+ end
4
+
5
+ module Beaker
6
+ class TestCase
7
+ include BeakerWindows::Path
8
+ include BeakerWindows::Powershell
9
+ include BeakerWindows::Registry
10
+ include BeakerWindows::WindowsFeature
11
+ end
12
+ end
@@ -0,0 +1,111 @@
1
+ module BeakerWindows
2
+ module Path
3
+
4
+ # Combine string containing paths with mixed separators. Coerce the path
5
+ # to Windows style automatically. (Configurable)
6
+ #
7
+ # ==== Attributes
8
+ #
9
+ # * +*paths+ - Two or more strings containing paths to be joined.
10
+ # * +opts+ - An options hash - not required
11
+ # * +:path_sep+ - The desired path separator to use. (Default: "\")
12
+ # * +:strip_drive+ - A boolean flag indicating of the drive letter should be stripped.
13
+ #
14
+ # ==== Returns
15
+ #
16
+ # +string+ - An absolute path to the manifests for an environment on the master host.
17
+ #
18
+ # ==== Raises
19
+ #
20
+ # +ArgumentError+ - Too few arguments.
21
+ #
22
+ # ==== Example
23
+ #
24
+ # join_path('c:\meow', 'cats/', 'bats\')
25
+ # join_path('c:\dog', 'bark', :strip_drive => true)
26
+ def join_path(*args)
27
+ # Init
28
+ opts = args.last.is_a?(Hash) ? args.pop : {}
29
+ opts[:path_sep] ||= "\\"
30
+ opts[:strip_drive] ||= false
31
+
32
+ combined_path = ''
33
+
34
+ # Verify that the user provided at least two paths to combine
35
+ raise(ArgumentError, "Too few arguments") if args.length < 2
36
+
37
+ # Combine the paths
38
+ args.each do |path|
39
+ # Verify that the provided args are strings
40
+ raise(ArgumentError, "Non-string provided as path") unless path.is_a?(String)
41
+
42
+ combined_path << opts[:path_sep].to_s unless combined_path.empty?
43
+ combined_path << path.gsub(/(\\|\/)/, opts[:path_sep].to_s)
44
+ end
45
+
46
+ # Remove duplicate path separators
47
+ combined_path.gsub!(/(\\|\/){2,}/, opts[:path_sep].to_s)
48
+
49
+ # Strip the drive letter if needed
50
+ combined_path.sub!(/^\w:/, '') if opts[:strip_drive]
51
+
52
+ return combined_path
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ module Beaker
59
+ module DSL
60
+ module Assertions
61
+
62
+ # Assert that a Windows file/registry path is valid on a host.
63
+ #
64
+ # ==== Attributes
65
+ #
66
+ # * +host+ - A Windows Beaker host running PowerShell.
67
+ # * +path+ - A path representing either a file system or registry path.
68
+ # If asserting registry paths they must be perpended with the correct hive.
69
+ # * +path_type+ - The type of path expected.
70
+ # * +:any+ - Can be a container or leaf. (Default)
71
+ # * +:container+ - Path must be a file system folder or registry key.
72
+ # * +:leaf+ - Path must be a file system file or registry value.
73
+ #
74
+ # ==== Raises
75
+ #
76
+ # +ArgumentError+ - An invalid path type specified.
77
+ # +Minitest::Assertion+ - Path does not exist or is the wrong type.
78
+ #
79
+ # ==== Example
80
+ #
81
+ # assert_win_path_on(host, 'C:\Windows')
82
+ # assert_win_path_on(host, 'C:\Windows\System32', :container)
83
+ # assert_win_path_on(host, 'C:\Windows\System32\kernel32.dll', :leaf)
84
+ # assert_win_path_on(host, 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRoot')
85
+ def assert_win_path_on(host, path, path_type=:any)
86
+ # Init
87
+ ps_cmd = "Test-Path -Path '#{path}' -Type "
88
+
89
+ # Expected path type
90
+ case path_type
91
+ when :any
92
+ ps_cmd << 'Any'
93
+ when :container
94
+ ps_cmd << 'Container'
95
+ when :leaf
96
+ ps_cmd << 'Leaf'
97
+ else
98
+ raise(ArgumentError, 'An invalid path type specified!')
99
+ end
100
+
101
+ # Test path
102
+ result = on(host, exec_ps_cmd(ps_cmd,
103
+ :verify_cmd => true,
104
+ :EncodedCommand => true),
105
+ :accept_all_exit_codes => true)
106
+ assert(0 == result.exit_code, 'Path does not exist or is the wrong type!')
107
+ end
108
+
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,111 @@
1
+ module BeakerWindows
2
+ module Powershell
3
+
4
+ # Executes a PowerShell command on a host. Allow validation of command execution and fail if
5
+ # PowerShell command throws an exception. Note: if quotes are required then the single quote
6
+ # should be preferred. If double quotes are required you will need to double escape the quotes!
7
+ #
8
+ # ==== Attributes
9
+ #
10
+ # * +command+ - The PowerShell command to execute on the host.
11
+ # * +opts+ - Either command-line options to pass to PowerShell or the following:
12
+ # * +:excep_fail+ - Command will return exit code 1 if an exception is caught.
13
+ # (Default: true)
14
+ # * +:verify_cmd+ - Command will return exit code 1 if the outcome of the command
15
+ # is "$false". (Default: false)
16
+ # * +:EncodedCommand+ - Will encode the command in base64. Useful for commands
17
+ # with nested quoting or containing Unicode characters. (Default: false)
18
+ #
19
+ # ==== Returns
20
+ #
21
+ # +Beaker::Command+ - An object for executing powershell commands on a host
22
+ #
23
+ # ==== Raises
24
+ #
25
+ # +nil+
26
+ #
27
+ # ==== Example
28
+ #
29
+ # on(hosts, exec_ps_cmd("Set-Content -path 'fu.txt' -value 'fu'"))
30
+ # on(hosts, exec_ps_cmd("Set-Content -path 'fu.txt' -value 'fu'", :ExecutionPolicy => 'Unrestricted')
31
+ # on(hosts, exec_ps_cmd("Set Content -path 'fu.txt', -value 'fu'", :EncodedCommand => true))
32
+ # on(hosts, exec_ps_cmd("1 -eq 2", :verify_cmd => true))
33
+ # on(hosts, exec_ps_cmd("does.not.exist", :excep_fail => true))
34
+ def exec_ps_cmd(command, opts={})
35
+ # Init
36
+ opts[:verify_cmd] ||= false
37
+ opts[:excep_fail] = true if opts[:excep_fail].nil?
38
+
39
+ ps_opts = {} # Command-line options to pass to PowerShell
40
+
41
+ # Determine how the command should be wrapped for execution
42
+ if opts[:verify_cmd]
43
+ command = "if ( #{command} ) { exit 0 } else { exit 1 }"
44
+ end
45
+
46
+ if opts[:excep_fail]
47
+ # Only prefix escape character if not encoded
48
+ var_accessor = opts[:EncodedCommand] ? '$_' : '\$_'
49
+
50
+ command = "try { #{command} } catch { Write-Host #{var_accessor}.Exception.Message; exit 1 }"
51
+ end
52
+
53
+ # Wrap the command in quotes when not encoded
54
+ command = "\"#{command}\"" unless opts[:EncodedCommand]
55
+
56
+ # Coerce all keys to be strings.
57
+ opts.each do |k, v|
58
+ ps_opts[k.to_s] = v unless (k == :verify_cmd) || (k == :excep_fail)
59
+ end
60
+
61
+ return powershell(command, ps_opts)
62
+ end
63
+
64
+ # Execute a PowerShell script on a remote machine. (This method support native Unicode)
65
+ # Note: This method fails on Windows 2008 R2! See BKR-293 for more details.
66
+ #
67
+ # ==== Attributes
68
+ #
69
+ # * +hosts+ - A Windows Beaker host(s) running PowerShell.
70
+ # * +ps_script+ - A PowerShell script to execute on the remote host.
71
+ #
72
+ # ==== Returns
73
+ #
74
+ # +Beaker::Result+
75
+ #
76
+ # ==== Raises
77
+ #
78
+ # +nil+
79
+ #
80
+ # ==== Examples
81
+ #
82
+ # exec_ps_script_on(host, 'Write-Host Hello')
83
+ def exec_ps_script_on(hosts, ps_script, &block)
84
+ #Init
85
+ script_path = "C:/Windows/Temp/beaker_powershell_script_#{Time.now.to_i}.ps1"
86
+ utf8_ps_script = "\xEF\xBB\xBF".force_encoding('UTF-8') + ps_script.force_encoding('UTF-8')
87
+
88
+ block_on(hosts) do |host|
89
+ #Create remote file with UTF-8 BOM
90
+ create_remote_file(host, script_path, utf8_ps_script)
91
+
92
+ #Execute PowerShell script on host
93
+ @result = on(host, powershell('', {'File' => script_path}), :accept_all_exit_codes => true)
94
+
95
+ #Also, let additional checking be performed by the caller.
96
+ if block_given?
97
+ case block.arity
98
+ #block with arity of 0, just hand back yourself
99
+ when 0
100
+ yield self
101
+ #block with arity of 1 or greater, hand back the result object
102
+ else
103
+ yield @result
104
+ end
105
+ end
106
+ @result
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,251 @@
1
+ module BeakerWindows
2
+ module Registry
3
+
4
+ # Get the data from a registry value.
5
+ #
6
+ # ==== Attributes
7
+ #
8
+ # * +hive+ - A symbol representing the following hives:
9
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
10
+ # * +:hkcu+ - HKEY_CURRENT_USER.
11
+ # * +:hku+ - HKEY_USERS.
12
+ #
13
+ # ==== Returns
14
+ #
15
+ # +String+ - A string representing the PowerShell hive path.
16
+ #
17
+ # ==== Raises
18
+ #
19
+ # +ArgumentError+ - Invalid registry hive specified!
20
+ #
21
+ # ==== Example
22
+ #
23
+ # get_registry_value_on(host, :hklm, "SOFTWARE\Microsoft\Windows NT\CurrentVersion", "SystemRoot")
24
+ def _get_hive(hive)
25
+ # Translate hives.
26
+ case hive
27
+ when :hklm
28
+ return "HKLM:\\"
29
+ when :hkcu
30
+ return "HKCU:\\"
31
+ when :hku
32
+ return "HKU:\\"
33
+ else
34
+ raise(ArgumentError, 'Invalid registry hive specified!')
35
+ end
36
+ end
37
+
38
+ # Get the data from a registry value.
39
+ #
40
+ # ==== Attributes
41
+ #
42
+ # * +host+ - A Windows Beaker host.
43
+ # * +hive+ - The hive containing the registry value. Allowed values:
44
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
45
+ # * +:hkcu+ - HKEY_CURRENT_USER.
46
+ # * +:hku+ - HKEY_USERS.
47
+ # * +path+ - The key containing the desired registry value.
48
+ # * +value+ - The name of the registry value.
49
+ #
50
+ # ==== Returns
51
+ #
52
+ # +String+ - A string representing the registry value data. (Always returns a string
53
+ # even for DWORD/QWORD and Binary value types.)
54
+ #
55
+ # ==== Raises
56
+ #
57
+ # +ArgumentError+ - Invalid registry hive specified!
58
+ # +RuntimeError+ - The specified key or path does not exist.
59
+ #
60
+ # ==== Example
61
+ #
62
+ # get_registry_value_on(host, :hklm, "SOFTWARE\Microsoft\Windows NT\CurrentVersion", "SystemRoot")
63
+ def get_registry_value_on(host, hive, path, value)
64
+ # Init
65
+ ps_cmd = "(Get-Item -Path '#{_get_hive(hive)}#{path}').GetValue('#{value}')"
66
+
67
+ # Parse output
68
+ result = on(host, exec_ps_cmd(ps_cmd, :EncodedCommand => true), :accept_all_exit_codes => true)
69
+
70
+ raise(RuntimeError, 'Registry path or value does not exist!') if result.exit_code == 1
71
+
72
+ return result.stdout.rstrip
73
+ end
74
+
75
+ # Create or update the data for a registry value.
76
+ #
77
+ # ==== Attributes
78
+ #
79
+ # * +host+ - A Windows Beaker host.
80
+ # * +hive+ - The hive containing the registry value. Allowed values:
81
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
82
+ # * +:hkcu+ - HKEY_CURRENT_USER.
83
+ # * +:hku+ - HKEY_USERS.
84
+ # * +path+ - The key containing the desired registry value.
85
+ # * +value+ - The name of the registry value.
86
+ # * +data+ - The data for the specified registry value.
87
+ # * +data_type+ - The data type for the specified registry value:
88
+ # * +:string+ - REG_SZ .
89
+ # * +:multi+ - REG_MULTI_SZ.
90
+ # * +:expand+ - REG_EXPAND_SZ.
91
+ # * +:dword+ - REG_DWORD.
92
+ # * +:qword+ - REG_QWORD.
93
+ # * +:bin+ - REG_BINARY. This needs to be a string of comma-separated hex values.
94
+ # (example: "be,ef,f0,0d")
95
+ #
96
+ # ==== Raises
97
+ #
98
+ # +ArgumentError+ - Invalid registry hive specified!
99
+ # +ArgumentError+ - Invalid format for binary data!
100
+ # +ArgumentError+ - Invalid data type specified!
101
+ # +RuntimeError+ - The specified key or path does not exist.
102
+ #
103
+ # ==== Example
104
+ #
105
+ # set_registry_value_on(host, :hkcu, 'SOFTWARE\test_key', 'string_value', 'test_data')
106
+ # set_registry_value_on(host, :hkcu, 'SOFTWARE\test_key', 'dword_value', 255, :dword)
107
+ # set_registry_value_on(host, :hkcu, 'SOFTWARE\test_key', 'bin_value', 'be,ef,f0,0d', :bin)
108
+ def set_registry_value_on(host, hive, path, value, data, data_type=:string)
109
+ # Init
110
+ ps_cmd = "New-ItemProperty -Force -Path '#{_get_hive(hive)}#{path}' -Name '#{value}'"
111
+
112
+ # Data type coercion.
113
+ case data_type
114
+ when :string
115
+ ps_cmd << " -Value '#{data.to_s}' -PropertyType String"
116
+ when :multi
117
+ ps_cmd << " -Value '#{data.to_s}' -PropertyType MultiString"
118
+ when :expand
119
+ ps_cmd << " -Value '#{data.to_s}' -PropertyType ExpandString"
120
+ when :dword
121
+ ps_cmd << " -Value #{data.to_s} -PropertyType DWord"
122
+ when :qword
123
+ ps_cmd << " -Value #{data.to_s} -PropertyType QWord"
124
+ when :bin
125
+ raise(ArgumentError, 'Invalid format for binary data!') unless data =~ /^(,?[\da-f]{2})+$/
126
+
127
+ hexified = ''
128
+ data.split(',').each do |hex|
129
+ hexified << ',' unless hexified.empty?
130
+ hexified << "0x#{hex}"
131
+ end
132
+
133
+ ps_cmd << " -Value ([byte[]](#{hexified})) -PropertyType Binary"
134
+ else
135
+ raise(ArgumentError, 'Invalid data type specified!')
136
+ end
137
+
138
+ # Parse output
139
+ result = on(host, exec_ps_cmd(ps_cmd, :EncodedCommand => true), :accept_all_exit_codes => true)
140
+
141
+ raise(RuntimeError, 'Registry path or value does not exist!') if result.exit_code == 1
142
+ end
143
+
144
+ # Remove a registry value.
145
+ #
146
+ # ==== Attributes
147
+ #
148
+ # * +host+ - A Windows Beaker host.
149
+ # * +hive+ - The hive containing the registry value. Allowed values:
150
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
151
+ # * +:hkcu+ - HKEY_CURRENT_USER.
152
+ # * +:hku+ - HKEY_USERS.
153
+ # * +path+ - The key containing the desired registry value.
154
+ # * +value+ - The name of the registry value.
155
+ #
156
+ # ==== Returns
157
+ #
158
+ # +String+ - A string representing the registry value data. (Always returns a string
159
+ # even for DWORD/QWORD and Binary value types.)
160
+ #
161
+ # ==== Raises
162
+ #
163
+ # +ArgumentError+ - Invalid registry hive specified!
164
+ # +RuntimeError+ - The specified key or path does not exist.
165
+ #
166
+ # ==== Example
167
+ #
168
+ # remove_registry_value_on(host, :hkcu, 'SOFTWARE\test_key', 'string_value')
169
+ def remove_registry_value_on(host, hive, path, value)
170
+ # Init
171
+ ps_cmd = "Remove-ItemProperty -Force -Path '#{_get_hive(hive)}#{path}' -Name '#{value}'"
172
+
173
+ # Parse output
174
+ result = on(host, exec_ps_cmd(ps_cmd, :EncodedCommand => true), :accept_all_exit_codes => true)
175
+
176
+ raise(RuntimeError, 'Registry path or value does not exist!') if result.exit_code == 1
177
+ end
178
+
179
+ # Create a new registry key. If the key already exists then this method will
180
+ # silently fail. This method will create parent intermediate parent keys if they
181
+ # do not exist.
182
+ #
183
+ # ==== Attributes
184
+ #
185
+ # * +host+ - A Windows Beaker host.
186
+ # * +hive+ - The hive containing the registry value. Allowed values:
187
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
188
+ # * +:hkcu+ - HKEY_CURRENT_USER.
189
+ # * +:hku+ - HKEY_USERS.
190
+ # * +path+ - The path of the registry key to create.
191
+ #
192
+ # ==== Raises
193
+ #
194
+ # +ArgumentError+ - Invalid registry hive specified!
195
+ # +RuntimeError+ - The specified key or path does not exist.
196
+ #
197
+ # ==== Example
198
+ #
199
+ # new_registry_key_on(host, :hkcu, 'SOFTWARE\some_new_key')
200
+ def new_registry_key_on(host, hive, path)
201
+ # Init
202
+ ps_cmd = "New-Item -Force -Path '#{_get_hive(hive)}#{path}'"
203
+
204
+ # Parse output
205
+ result = on(host, exec_ps_cmd(ps_cmd, :EncodedCommand => true), :accept_all_exit_codes => true)
206
+
207
+ raise(RuntimeError, 'Registry path or value does not exist!') if result.exit_code == 1
208
+ end
209
+
210
+ # Remove a registry key. The method will not remove a registry key if the key contains
211
+ # nested subkeys and values. Use the "recurse" argument to force deletion of nested
212
+ # registry keys.
213
+ #
214
+ # ==== Attributes
215
+ #
216
+ # * +host+ - A Windows Beaker host.
217
+ # * +hive+ - The hive containing the registry value. Allowed values:
218
+ # * +:hklm+ - HKEY_LOCAL_MACHINE.
219
+ # * +:hkcu+ - HKEY_CURRENT_USER.
220
+ # * +:hku+ - HKEY_USERS.
221
+ # * +path+ - The key containing the desired registry value.
222
+ # * +recurse+ - Recursively delete nested subkeys and values. (Default: false)
223
+ #
224
+ # ==== Returns
225
+ #
226
+ # +String+ - A string representing the registry value data. (Always returns a string
227
+ # even for DWORD/QWORD and Binary value types.)
228
+ #
229
+ # ==== Raises
230
+ #
231
+ # +ArgumentError+ - Invalid registry hive specified!
232
+ # +RuntimeError+ - The specified key or path does not exist.
233
+ #
234
+ # ==== Example
235
+ #
236
+ # remove_registry_key_on(host, :hkcu, 'SOFTWARE\test_key')
237
+ def remove_registry_key_on(host, hive, path, recurse=false)
238
+ # Init
239
+ ps_cmd = "Remove-Item -Force -Path '#{_get_hive(hive)}#{path}'"
240
+
241
+ # Recursively delete key if requested
242
+ ps_cmd << " -Recurse" if recurse
243
+
244
+ # Parse output
245
+ result = on(host, exec_ps_cmd(ps_cmd, :EncodedCommand => true), :accept_all_exit_codes => true)
246
+
247
+ raise(RuntimeError, 'Registry path or value does not exist!') if result.exit_code == 1
248
+ end
249
+
250
+ end
251
+ end