beaker-windows 0.6.2

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.
@@ -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