Added windows support to the processes resource

Signed-off-by: username-is-already-taken2 <digitalgaz@hotmail.com>
This commit is contained in:
username-is-already-taken2 2017-06-04 21:03:04 +01:00
parent 75728f786c
commit 9d9baeb09f
5 changed files with 90 additions and 12 deletions

View file

@ -60,18 +60,33 @@ The following examples show how to use this InSpec audit resource.
its('list.length') { should eq 1 }
end
### Test if the init process is owned by the root user
### Test if the process is owned by a specifc user
describe processes('init') do
its('users') { should eq ['root'] }
end
describe processes('winlogon') do
its('users') { should cmp "NT AUTHORITY\\SYSTEM" }
end
### Test if a high-priority process is running
describe processes('some_process') do
describe processes('linux_process') do
its('states') { should eq ['R<'] }
end
describe processes('windows_process') do
its('labels') { should cmp "High" }
end
### Test if a process exists on the system
describe processes('some_process') do
it { should exist }
end
### Test for a process using a specific Regexp
If the process name is too common for a string to uniquely find it,
@ -81,3 +96,28 @@ needed.
describe processes(Regexp.new("/usr/local/bin/swap -d")) do
its('list.length') { should eq 1 }
end
### Notes for auditing Windows systems
Sometimes with system properties there isn't a direct comparison between different operating systems.
Most of the `property_name`'s do align between the different OS's.
There are however some exception's, for example, within linux `states` offers multiple properties.
Windows doesn't have direct comparison that is a single property so instead `states` is mapped to the property of `Responding`, This is a boolean true/false flag to help determine if the process is hung.
Below is a mapping table to help you understand what property the unix field maps to the windows `Get-Process` Property
| *unix ps field* | *windows PowerShell Property* |
|:---------------:|:-----------------------------:|
|labels |PriorityClass|
|pids |Id|
|cpus |CPU|
|mem |PM|
|vsz |VirtualMemorySize|
|rss |NPM|
|tty |SessionId|
|states |Responding|
|start |StartTime|
|time |TotalProcessorTime|
|users |UserName|
|commands |Path|

View file

@ -25,15 +25,24 @@ module Inspec::Resources
@grep = grep
# turn into a regexp if it isn't one yet
if grep.class == String
grep = '(/[^/]*)*' + grep if grep[0] != '/'
grep = Regexp.new('^' + grep + '(\s|$)')
# if windows ignore case as we can't make up our minds
if inspec.os.windows?
grep = '(?i)' + grep
else
grep = '(/[^/]*)*' + grep unless grep[0] == '/'
grep = '^' + grep + '(\s|$)'
end
grep = Regexp.new(grep)
end
all_cmds = ps_axo
@list = all_cmds.find_all do |hm|
hm[:command] =~ grep
end
end
return skip_resource 'The `processes` resource is not supported on your OS yet.' if inspec.os.windows?
def exists?
!@list.empty?
end
def to_s
@ -74,6 +83,10 @@ module Inspec::Resources
if os.linux?
command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
regex = /^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
elsif os.windows?
command = '$Proc = Get-Process -IncludeUserName | Where-Object {$_.Path -ne $null } | Select-Object PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path | ConvertTo-Csv -NoTypeInformation;$Proc.Replace("""","").Replace("`r`n","`n")'
# Wanted to use /(?:^|,)([^,]*)/; works on rubular.com not sure why here?
regex = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
else
command = 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command'
regex = /^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
@ -95,7 +108,7 @@ module Inspec::Resources
end.compact
lines.map do |m|
a = m.to_a[1..-1] # grab all matching groups
a.unshift(nil) unless os.linux?
a.unshift(nil) unless os.linux? || os.windows?
a[1] = a[1].to_i
a[4] = a[4].to_i
a[5] = a[5].to_i

View file

@ -303,10 +303,10 @@ class MockLoader
'crontab -l' => cmd.call('crontab-root'),
# crontab display for non-current user
'crontab -l -u foouser' => cmd.call('crontab-foouser'),
# zfs output for dataset tank/tmp
'/sbin/zfs get -Hp all tank/tmp' => cmd.call('zfs-get-all-tank-tmp'),
# zfs output for pool tank
'/sbin/zpool get -Hp all tank' => cmd.call('zpool-get-all-tank'),
# zfs output for dataset tank/tmp
'/sbin/zfs get -Hp all tank/tmp' => cmd.call('zfs-get-all-tank-tmp'),
# zfs output for pool tank
'/sbin/zpool get -Hp all tank' => cmd.call('zpool-get-all-tank'),
# docker
"docker ps -a --no-trunc --format '{{ json . }}'" => cmd.call('docker-ps-a'),
"docker version --format '{{ json . }}'" => cmd.call('docker-version'),
@ -314,8 +314,9 @@ class MockLoader
"docker inspect 71b5df59442b" => cmd.call('docker-inspec'),
# docker images
"83c36bfade9375ae1feb91023cd1f7409b786fd992ad4013bf0f2259d33d6406" => cmd.call('docker-images'),
}
# get-process cmdlet for processes resource
'$Proc = Get-Process -IncludeUserName | Where-Object {$_.Path -ne $null } | Select-Object PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path | ConvertTo-Csv -NoTypeInformation;$Proc.Replace("""","").Replace("`r`n","`n")' => cmd.call('get-process_processes')
}
@backend
end

View file

@ -0,0 +1,3 @@
PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path
Normal,2456,0.296875,4808704,118202368,14576,1,True,5/31/2017 9:13:17 AM,00:00:00.2968750,WINVAGR-QQQNHPN\Administrator,C:\Windows\system32\mmc.exe
High,396,0.15625,1323008,53710848,7776,1,True,5/31/2017 9:12:56 AM,00:00:00.1562500,NT AUTHORITY\SYSTEM,C:\Windows\system32\winlogon.exe

View file

@ -109,6 +109,7 @@ describe 'Inspec::Resources::Processes' do
resource = MockLoader.new(:centos6).load_resource('processes', 'postgres: bifrost bifrost')
_(resource.users.sort).must_equal ['opscode-pgsql']
_(resource.states.sort).must_equal ['Ss']
_(resource.exists?).must_equal true
end
it 'command name matches with output (string)' do
@ -120,4 +121,24 @@ describe 'Inspec::Resources::Processes' do
resource = MockLoader.new(:centos6).load_resource('processes', /mysqld/)
_(resource.to_s).must_equal 'Processes /mysqld/'
end
it 'command name matches with output (string)' do
resource = MockLoader.new(:windows).load_resource('processes', 'winlogon.exe')
_(resource.to_s).must_equal 'Processes winlogon.exe'
end
it 'retrieves the users and states as arrays on windows os' do
resource = MockLoader.new(:windows).load_resource('processes', 'winlogon.exe')
_(resource.users.sort).must_equal ['NT AUTHORITY\\SYSTEM']
end
it 'process should exist' do
resource = MockLoader.new(:windows).load_resource('processes', 'winlogon.exe')
_(resource.exists?).must_equal true
end
it 'process should_not exist' do
resource = MockLoader.new(:windows).load_resource('processes', 'unicorn.exe')
_(resource.exists?).must_equal false
end
end