Merge pull request #233 from chef/alexpop/registry_key

improvement: rewrite registry_key resource, serverspec compatibility and add integration tests
This commit is contained in:
Dominik Richter 2015-11-17 23:09:50 +01:00
commit 254f672f37
9 changed files with 249 additions and 25 deletions

View file

@ -14,6 +14,8 @@
class Cmd < Inspec.resource(1)
name 'command'
attr_reader :command
def initialize(cmd)
@command = cmd
end

View file

@ -22,33 +22,129 @@ class RegistryKey < Inspec.resource(1)
@reg_key = reg_key
end
def registry_value(path, key)
cmd = "(Get-Item 'Registry::#{path}').GetValue('#{key}')"
command_result ||= inspec.command(cmd)
val = { exit_code: command_result.exit_status.to_i, data: command_result.stdout }
val
def exists?
!registry_value(@reg_key).nil?
end
def convert_value(value)
val = value.strip
val = val.to_i if val.match(/^\d+$/)
val
def has_value?(value)
val = registry_value(@reg_key)
!val.nil? && val['(default)'.to_s]['value'] == value ? true : false
end
def has_property?(property_name, property_type = nil)
val = registry_value(@reg_key)
!val.nil? && !val[property_name.to_s].nil? && (property_type.nil? || val[property_name.to_s]['type'] == map2type(property_type)) ? true : false
end
# deactivate rubocop, because we need to stay compatible with Serverspe
# rubocop:disable Style/OptionalArguments
def has_property_value?(property_name, property_type = nil, value)
# rubocop:enable Style/OptionalArguments
val = registry_value(@reg_key)
# convert value to binary if required
value = value.bytes if !property_type.nil? && map2type(property_type) == 3 && !value.is_a?(Array)
!val.nil? && val[property_name.to_s]['value'] == value && (property_type.nil? || val[property_name.to_s]['type'] == map2type(property_type)) ? true : false
end
# returns nil, if not existant or value
def method_missing(meth)
# get data
val = registry_value(@reg_key, meth)
# verify data
if val[:exit_code] == 0
return convert_value(val[:data])
else
return nil
end
val = registry_value(@reg_key)
return nil if val.nil?
val[meth.to_s]['value']
end
def to_s
"Registry Key #{@name}"
end
private
def registry_value(path)
return @registy_cache if defined?(@registy_cache)
# load registry key and all properties
script = <<-EOH
$reg = Get-Item 'Registry::#{path}'
$object = New-Object -Type PSObject
$reg.Property | ForEach-Object {
$key = $_
if ("(default)".Equals($key)) { $key = '' }
$value = New-Object psobject -Property @{
"value" = $reg.GetValue($key);
"type" = $reg.GetValueKind($key);
}
$object | Add-Member MemberType NoteProperty Name $_ Value $value
}
$object | ConvertTo-Json
EOH
cmd = inspec.script(script)
# cannot rely on exit code for now, successful command returns exit code 1
# return nil if cmd.exit_status != 0, try to parse json
begin
@registy_cache = JSON.parse(cmd.stdout)
rescue JSON::ParserError => _e
@registy_cache = nil
end
@registy_cache
end
# Registry key value types
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
# REG_NONE 0
# REG_SZ 1
# REG_EXPAND_SZ 2
# REG_BINARY 3
# REG_DWORD 4
# REG_DWORD_LITTLE_ENDIAN 4
# REG_DWORD_BIG_ENDIAN 5
# REG_LINK 6
# REG_MULTI_SZ 7
# REG_RESOURCE_LIST 8
# REG_FULL_RESOURCE_DESCRIPTOR 9
# REG_RESOURCE_REQUIREMENTS_LIST 10
# REG_QWORD 11
# REG_QWORD_LITTLE_ENDIAN 11
def map2type(symbol)
options = {}
# chef symbols, we prefer those
options[:binary] = 3
options[:string] = 1
options[:multi_string] = 7
options[:expand_string] = 2
options[:dword] = 4
options[:dword_big_endian] = 5
options[:qword] = 11
# serverspec symbols
options[:type_string] = 1
options[:type_binary] = 3
options[:type_dword] = 4
options[:type_qword] = 11
options[:type_multistring] = 7
options[:type_expandstring] = 2
options[symbol]
end
end
# for compatability with serverspec
# this is deprecated syntax and will be removed in future versions
class WindowsRegistryKey < RegistryKey
name 'windows_registry_key'
def initialize(name)
deprecated
super(name)
end
def deprecated
warn '[DEPRECATION] `yumrepo(reponame)` is deprecated. Please use `yum.repo(reponame)` instead.'
end
end

View file

@ -6,7 +6,6 @@
class Script < Cmd
name 'script'
attr_accessor :command
def initialize(script)
case inspec.os[:family]
@ -19,8 +18,7 @@ class Script < Cmd
else
return skip_resource 'The `script` resource is not supported on your OS yet.'
end
@command = cmd
super(cmd)
end
# we cannot determine if a command exists, because that does not work for scripts

View file

@ -109,7 +109,8 @@ class MockLoader
'secedit /export /cfg win_secpol.cfg' => cmd.call('success'),
'del win_secpol.cfg' => cmd.call('success'),
'env' => cmd.call('env'),
'(Get-Item \'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule\').GetValue(\'Start\')' => cmd.call('reg_schedule'),
# registry key test
'2790db1e88204a073ed7fd3493f5445e5ce531afd0d2724a0e36c17110c535e6' => cmd.call('reg_schedule'),
'Auditpol /get /subcategory:\'User Account Management\' /r' => cmd.call('auditpol'),
'/sbin/auditctl -l' => cmd.call('auditctl'),
'yum -v repolist all' => cmd.call('yum-repolist-all'),

View file

@ -7,4 +7,5 @@
include_recipe('os_prepare::apt')
include_recipe('os_prepare::file')
include_recipe('os_prepare::package')
include_recipe('os_prepare::registry_key')
include_recipe('os_prepare::service')

View file

@ -0,0 +1,69 @@
# encoding: utf-8
# author: Alex Pop
# author: Christoph Hartmann
#
# change a few Windows registry keys for testing purposes
if node['platform_family'] == 'windows'
registry_key 'HKLM\System\Test' do
values [{
:name => '',
:type => :string,
:data => 'test'
},{
:name => 'string value',
:type => :string,
:data => nil
},{
:name => 'binary value',
:type => :binary,
:data => 'dfa0f066'
},{
:name => 'dword value',
:type => :dword,
:data => 0
},{
:name => 'qword value',
:type => :qword,
:data => 0
},{
:name => 'multistring value',
:type => :multi_string,
:data => ['test', 'multi','string','data']
}]
recursive true
action :create
end
registry_key 'HKLM\Software\Policies\Microsoft\Windows\EventLog\System' do
values [{ name: 'MaxSize', type: :dword, data: 67_108_864 }]
recursive true
action :create
end
registry_key 'HKLM\System\CurrentControlSet\Control\Session Manager' do
values [{ name: 'SafeDllSearchMode', type: :dword, data: 1 }]
recursive true
action :create
end
registry_key 'HKLM\System\CurrentControlSet\Services\LanManServer\Parameters' do
values [{ name: 'NullSessionShares', type: :multi_string, data: [] }]
recursive true
action :create
end
registry_key 'HKLM\Software\Policies\Microsoft\Internet Explorer\Main' do
values [{ name: 'Isolation64Bit', type: :dword, data: 1 }]
recursive true
action :create
end
registry_key 'HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' do
values [{ name: 'MinEncryptionLevel', type: :dword, data: 3 }]
recursive true
action :create
end
end

View file

@ -0,0 +1,53 @@
# encoding: utf-8
if os.windows?
describe registry_key('HKLM\System\Test') do
it { should exist }
it { should have_value('test') }
it { should have_property('binary value', :binary) }
it { should have_property('string value') }
it { should have_property('dword value', :dword) }
it { should have_property_value('multistring value', :multi_string, ['test', 'multi','string','data']) }
it { should have_property_value('qword value', :qword, 0) }
it { should have_property_value('binary value', :binary, 'dfa0f066') }
end
# serverspec compatability
describe windows_registry_key('HKLM\System\Test') do
it { should exist }
it { should have_value('test') }
it { should have_property('string value') }
it { should have_property('binary value', :type_binary) }
it { should have_property('dword value', :type_dword) }
it { should have_property_value('multistring value', :type_multistring, ['test', 'multi','string','data']) }
it { should have_property_value('qword value', :type_qword, 0) }
it { should have_property_value('binary value', :type_binary, 'dfa0f066') }
end
describe registry_key('HKLM\Software\Policies\Microsoft\Windows\EventLog\System') do
it { should exist }
its('MaxSize') { should_not eq nil }
end
describe registry_key('HKLM\System\CurrentControlSet\Control\Session Manager') do
it { should exist }
it { should_not have_property_value('SafeDllSearchMode', :type_dword, 0) }
end
describe registry_key('HKLM\System\CurrentControlSet\Services\LanManServer\Parameters') do
it { should exist }
its('NullSessionShares') { should eq [''] }
end
describe registry_key('HKLM\Software\Policies\Microsoft\Internet Explorer\Main') do
it { should exist }
its('Isolation64Bit') { should eq 1 }
end
describe registry_key('HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services') do
it { should exist }
its('MinEncryptionLevel') { should eq 3 }
end
end

View file

@ -1 +1,6 @@
2
{
"Start": {
"value": 2,
"type": 4
}
}

View file

@ -6,14 +6,13 @@ require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::RegistryKey' do
let(:resource) { load_resource('registry_key', 'Task Scheduler', 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule') }
let(:resource_without_name) { load_resource('registry_key', 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule') }
it 'read reg key with human readable name' do
resource = MockLoader.new(:windows).load_resource('registry_key', 'Task Scheduler', 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule')
_(resource.Start).must_equal 2
end
it 'read reg key without human readable name' do
resource_without_name = MockLoader.new(:windows).load_resource('registry_key', 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Schedule')
_(resource_without_name.Start).must_equal 2
end
end