inspec/lib/resources/processes.rb
Lynn Frank 2857d07151 Fixes resource examples
The opening and closing mechanic varied between all the various
resources. This changes them all to use a HEREDOC with a tilde
to remove leading whitespace. This removes the need for the
special method to trim the `#print_example` method from shell.

Signed-off-by: Franklin Webber <franklin.webber@gmail.com>
2019-03-19 11:25:41 -05:00

218 lines
6.7 KiB
Ruby

# encoding: utf-8
# copyright: 2015, Vulcano Security GmbH
require 'utils/filter'
require 'ostruct'
module Inspec::Resources
class Processes < Inspec.resource(1)
name 'processes'
supports platform: 'unix'
supports platform: 'windows'
desc 'Use the processes InSpec audit resource to test properties for programs that are running on the system.'
example <<~EXAMPLE
describe processes('mysqld') do
its('entries.length') { should eq 1 }
its('users') { should eq ['mysql'] }
its('states') { should include 'S' }
end
describe processes(/.+/).where { label != 'unconfined' && pid < 1000 } do
its('users') { should cmp [] }
end
# work with all processes
describe processes do
its('entries.length') { should be <= 100 }
end
EXAMPLE
def initialize(grep = /.*/)
@grep = grep
# turn into a regexp if it isn't one yet
if grep.class == String
# 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
def exists?
!@list.empty?
end
def to_s
"Processes #{@grep.class == String ? @grep : @grep.inspect}"
end
def list
warn '[DEPRECATION] `processes.list` is deprecated. Please use `processes.entries` instead. It will be removed in version 4.0.'
@list
end
filter = FilterTable.create
filter.register_column(:labels, field: 'label')
.register_column(:pids, field: 'pid')
.register_column(:cpus, field: 'cpu')
.register_column(:mem, field: 'mem')
.register_column(:vsz, field: 'vsz')
.register_column(:rss, field: 'rss')
.register_column(:tty, field: 'tty')
.register_column(:states, field: 'stat')
.register_column(:start, field: 'start')
.register_column(:time, field: 'time')
.register_column(:users, field: 'user')
.register_column(:commands, field: 'command')
.install_filter_methods_on_resource(self, :filtered_processes)
private
def filtered_processes
@list
end
def ps_axo
os = inspec.os
if os.linux?
command, regex, field_map = ps_configuration_for_linux
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 = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
field_map = {
pid: 2,
cpu: 3,
mem: 4,
vsz: 5,
rss: 6,
tty: 7,
stat: 8,
start: 9,
time: 10,
user: 11,
command: 12,
}
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+(.*)$/
field_map = {
pid: 1,
cpu: 2,
mem: 3,
vsz: 4,
rss: 5,
tty: 6,
stat: 7,
start: 8,
time: 9,
user: 10,
command: 11,
}
end
build_process_list(command, regex, field_map)
end
def ps_configuration_for_linux
if busybox_ps?
command = 'ps -o pid,vsz,rss,tty,stat,time,ruser,args'
regex = /^\s*(\d+)\s+(\d+(?:\.\d+)?[gm]?)\s+(\d+(?:\.\d+)?[gm]?)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/
field_map = {
pid: 1,
vsz: 2,
rss: 3,
tty: 4,
stat: 5,
time: 6,
user: 7,
command: 8,
}
else
command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
field_map = {
label: 1,
pid: 2,
cpu: 3,
mem: 4,
vsz: 5,
rss: 6,
tty: 7,
stat: 8,
start: 9,
time: 10,
user: 11,
command: 12,
}
end
[command, regex, field_map]
end
def busybox_ps?
@busybox_ps ||= inspec.command('ps --help').stderr.include?('BusyBox')
end
def convert_to_kilobytes(param)
return param.to_i unless param.is_a?(String)
if param.end_with?('g')
(param[0..-2].to_f * 1024 * 1024).to_i
elsif param.end_with?('m')
(param[0..-2].to_f * 1024).to_i
else
param.to_i
end
end
def build_process_list(command, regex, field_map)
cmd = inspec.command(command)
all = cmd.stdout.split("\n")[1..-1]
return [] if all.nil?
# map all the process lines into match objects, fetch the available fields,
# and then build an OpenStruct of the process data for each process
all.map do |line|
line = line.match(regex)
# skip this line if we couldn't match the regular expression
next if line.nil?
# skip this entry if there's no command for this line
next if line[field_map[:command]].nil?
# build a hash of process data that we'll turn into a struct for FilterTable
process_data = {}
[:label, :pid, :cpu, :mem, :vsz, :rss, :tty, :stat, :start, :time, :user, :command].each do |param|
# not all operating systems support all fields, so skip the field if we don't have it
process_data[param] = line[field_map[param]] if field_map.key?(param)
end
# ensure pid, vsz, and rss are integers for backward compatibility
process_data[:pid] = process_data[:pid].to_i if process_data.key?(:pid)
# some ps variants (*cough* busybox) display vsz and rss as human readable MB or GB
[:vsz, :rss].each do |param|
next unless process_data.key?(param)
process_data[param] = convert_to_kilobytes(process_data[param])
end
# strip any newlines off the command
process_data[:command].strip!
# return an OpenStruct of the process for future use by FilterTable
OpenStruct.new(process_data)
end.compact
end
end
end