inspec 1.40.0 → 1.41.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -9
- data/docs/matchers.md +18 -0
- data/docs/plugin_kitchen_inspec.md +18 -24
- data/docs/profiles.md +39 -2
- data/docs/resources/aide_conf.md.erb +18 -28
- data/docs/resources/apache_conf.md.erb +19 -33
- data/docs/resources/apt.md.erb +22 -36
- data/docs/resources/audit_policy.md.erb +9 -24
- data/docs/resources/auditd.md.erb +9 -24
- data/docs/resources/auditd_conf.md.erb +20 -34
- data/docs/resources/auditd_rules.md.erb +8 -24
- data/docs/resources/bash.md.erb +4 -26
- data/docs/resources/bond.md.erb +25 -40
- data/docs/resources/bridge.md.erb +5 -25
- data/docs/resources/bsd_service.md.erb +5 -25
- data/docs/resources/command.md.erb +35 -50
- data/docs/resources/crontab.md.erb +9 -23
- data/docs/resources/csv.md.erb +12 -27
- data/docs/resources/dh_params.md +1 -0
- data/docs/resources/directory.md.erb +5 -25
- data/docs/resources/docker.md.erb +60 -57
- data/docs/resources/docker_container.md.erb +23 -19
- data/docs/resources/docker_image.md.erb +20 -16
- data/docs/resources/etc_fstab.md.erb +5 -2
- data/docs/resources/etc_group.md.erb +29 -45
- data/docs/resources/etc_hosts.md.erb +6 -0
- data/docs/resources/etc_hosts_allow.md.erb +6 -2
- data/docs/resources/etc_hosts_deny.md.erb +6 -2
- data/docs/resources/file.md.erb +198 -212
- data/docs/resources/firewalld.md.erb +7 -1
- data/docs/resources/gem.md.erb +21 -35
- data/docs/resources/group.md.erb +16 -30
- data/docs/resources/grub_conf.md.erb +9 -24
- data/docs/resources/host.md.erb +32 -49
- data/docs/resources/http.md.erb +38 -44
- data/docs/resources/iis_app.md.erb +25 -35
- data/docs/resources/iis_site.md.erb +26 -40
- data/docs/resources/inetd_conf.md.erb +27 -42
- data/docs/resources/ini.md.erb +9 -23
- data/docs/resources/interface.md.erb +5 -25
- data/docs/resources/iptables.md.erb +15 -29
- data/docs/resources/json.md.erb +12 -27
- data/docs/resources/kernel_module.md.erb +47 -61
- data/docs/resources/kernel_parameter.md.erb +15 -29
- data/docs/resources/key_rsa.md.erb +3 -0
- data/docs/resources/launchd_service.md.erb +5 -25
- data/docs/resources/limits_conf.md.erb +15 -29
- data/docs/resources/login_def.md.erb +15 -30
- data/docs/resources/mount.md.erb +18 -33
- data/docs/resources/mssql_session.md.erb +9 -12
- data/docs/resources/mysql_conf.md.erb +17 -32
- data/docs/resources/mysql_session.md.erb +15 -29
- data/docs/resources/nginx.md.erb +6 -0
- data/docs/resources/nginx_conf.md.erb +25 -20
- data/docs/resources/npm.md.erb +19 -35
- data/docs/resources/ntp_conf.md.erb +20 -37
- data/docs/resources/oneget.md.erb +15 -30
- data/docs/resources/oracledb_session.md.erb +9 -11
- data/docs/resources/os.md.erb +29 -43
- data/docs/resources/os_env.md.erb +29 -44
- data/docs/resources/package.md.erb +33 -42
- data/docs/resources/parse_config.md.erb +5 -25
- data/docs/resources/parse_config_file.md.erb +31 -43
- data/docs/resources/passwd.md.erb +24 -39
- data/docs/resources/pip.md.erb +20 -35
- data/docs/resources/port.md.erb +43 -57
- data/docs/resources/postgres_conf.md.erb +17 -31
- data/docs/resources/postgres_hba_conf.md.erb +26 -38
- data/docs/resources/postgres_ident_conf.md.erb +25 -37
- data/docs/resources/postgres_session.md.erb +15 -29
- data/docs/resources/powershell.md.erb +27 -42
- data/docs/resources/processes.md.erb +17 -33
- data/docs/resources/rabbitmq_config.md.erb +9 -24
- data/docs/resources/registry_key.md.erb +27 -42
- data/docs/resources/runit_service.md.erb +5 -25
- data/docs/resources/security_policy.md.erb +12 -27
- data/docs/resources/service.md.erb +27 -42
- data/docs/resources/shadow.md.erb +20 -35
- data/docs/resources/ssh_config.md.erb +19 -34
- data/docs/resources/sshd_config.md.erb +19 -34
- data/docs/resources/ssl.md.erb +39 -54
- data/docs/resources/sys_info.md.erb +12 -26
- data/docs/resources/systemd_service.md.erb +5 -25
- data/docs/resources/sysv_service.md.erb +5 -25
- data/docs/resources/upstart_service.md.erb +5 -25
- data/docs/resources/user.md.erb +29 -44
- data/docs/resources/users.md.erb +12 -26
- data/docs/resources/vbscript.md.erb +9 -24
- data/docs/resources/virtualization.md.erb +8 -23
- data/docs/resources/windows_feature.md.erb +15 -30
- data/docs/resources/windows_hotfix.md.erb +15 -9
- data/docs/resources/windows_task.md.erb +12 -26
- data/docs/resources/wmi.md.erb +9 -24
- data/docs/resources/x509_certificate.md.erb +4 -0
- data/docs/resources/xinetd_conf.md.erb +65 -80
- data/docs/resources/xml.md.erb +12 -26
- data/docs/resources/yaml.md.erb +12 -27
- data/docs/resources/yum.md.erb +37 -51
- data/docs/resources/zfs_dataset.md.erb +15 -26
- data/docs/resources/zfs_pool.md.erb +9 -20
- data/lib/inspec/backend.rb +8 -0
- data/lib/inspec/profile.rb +9 -1
- data/lib/inspec/shell.rb +13 -13
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +2 -0
- data/lib/resources/etc_hosts.rb +1 -1
- data/lib/resources/host.rb +4 -1
- data/lib/resources/http.rb +173 -23
- data/lib/resources/processes.rb +106 -20
- data/lib/resources/ssh_conf.rb +1 -1
- data/lib/resources/ssl.rb +4 -3
- data/lib/utils/object_traversal.rb +35 -10
- metadata +2 -2
@@ -6,6 +6,8 @@ title: About the zfs_dataset Resource
|
|
6
6
|
|
7
7
|
Use the `zfs_dataset` InSpec audit resource to test the ZFS datasets on FreeBSD systems.
|
8
8
|
|
9
|
+
<br>
|
10
|
+
|
9
11
|
## Syntax
|
10
12
|
|
11
13
|
A `zfs_dataset` resource block declares the ZFS dataset properties that should be tested:
|
@@ -20,32 +22,7 @@ where
|
|
20
22
|
* `MATCHER` is a valid matcher for this resource
|
21
23
|
* `'value'` is the value to be tested
|
22
24
|
|
23
|
-
|
24
|
-
## Matchers
|
25
|
-
|
26
|
-
This InSpec audit resource has the matchers listed below, in addition to dynamically exposing all ZFS dataset properties available (see: `man zfs` for the list of supported properties.)
|
27
|
-
|
28
|
-
### be
|
29
|
-
|
30
|
-
<%= partial "/shared/matcher_be" %>
|
31
|
-
|
32
|
-
### be_mounted
|
33
|
-
|
34
|
-
The `be_mounted` matcher tests if the dataset is accessible from the file system:
|
35
|
-
|
36
|
-
it { should be_mounted }
|
37
|
-
|
38
|
-
### cmp
|
39
|
-
|
40
|
-
<%= partial "/shared/matcher_cmp" %>
|
41
|
-
|
42
|
-
### eq
|
43
|
-
|
44
|
-
<%= partial "/shared/matcher_eq" %>
|
45
|
-
|
46
|
-
### match
|
47
|
-
|
48
|
-
<%= partial "/shared/matcher_match" %>
|
25
|
+
<br>
|
49
26
|
|
50
27
|
## Examples
|
51
28
|
|
@@ -61,3 +38,15 @@ The following examples show how to use this InSpec audit resource.
|
|
61
38
|
its('readonly') { should eq 'off' }
|
62
39
|
its('setuid') { should eq 'off' }
|
63
40
|
end
|
41
|
+
|
42
|
+
<br>
|
43
|
+
|
44
|
+
## Matchers
|
45
|
+
|
46
|
+
This InSpec audit resource has the matchers listed below, in addition to dynamically exposing all ZFS dataset properties available (see: `man zfs` for the list of supported properties). For a full list of available matchers please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
47
|
+
|
48
|
+
### be_mounted
|
49
|
+
|
50
|
+
The `be_mounted` matcher tests if the dataset is accessible from the file system:
|
51
|
+
|
52
|
+
it { should be_mounted }
|
@@ -6,6 +6,8 @@ title: About the zfs_pool Resource
|
|
6
6
|
|
7
7
|
Use the `zfs_pool` InSpec audit resource to test the ZFS pools on FreeBSD systems.
|
8
8
|
|
9
|
+
<br>
|
10
|
+
|
9
11
|
## Syntax
|
10
12
|
|
11
13
|
A `zfs_pool` resource block declares the ZFS pool properties that should be tested:
|
@@ -20,26 +22,7 @@ where
|
|
20
22
|
* `MATCHER` is a valid matcher for this resource
|
21
23
|
* `'value'` is the value to be tested
|
22
24
|
|
23
|
-
|
24
|
-
## Matchers
|
25
|
-
|
26
|
-
This InSpec audit resource has the matchers listed below, in addition to dynamically exposing all ZFS pool properties available (see: `man zpool` for the list of supported properties.)
|
27
|
-
|
28
|
-
### be
|
29
|
-
|
30
|
-
<%= partial "/shared/matcher_be" %>
|
31
|
-
|
32
|
-
### cmp
|
33
|
-
|
34
|
-
<%= partial "/shared/matcher_cmp" %>
|
35
|
-
|
36
|
-
### eq
|
37
|
-
|
38
|
-
<%= partial "/shared/matcher_eq" %>
|
39
|
-
|
40
|
-
### match
|
41
|
-
|
42
|
-
<%= partial "/shared/matcher_match" %>
|
25
|
+
<br>
|
43
26
|
|
44
27
|
## Examples
|
45
28
|
|
@@ -55,3 +38,9 @@ The following examples show how to use this InSpec audit resource.
|
|
55
38
|
its('listsnapshots') { should eq 'off' }
|
56
39
|
its('readonly') { should eq 'off' }
|
57
40
|
end
|
41
|
+
|
42
|
+
<br>
|
43
|
+
|
44
|
+
## Matchers
|
45
|
+
|
46
|
+
This InSpec audit resource dynamically exposes all ZFS pool properties available (see: `man zpool` for the list of supported properties). For a full list of available matchers please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
data/lib/inspec/backend.rb
CHANGED
@@ -17,6 +17,14 @@ module Inspec
|
|
17
17
|
Inspec::VERSION
|
18
18
|
end
|
19
19
|
|
20
|
+
# Determine whether the connection/transport is a local connection
|
21
|
+
# Useful for resources to modify behavior as necessary, such as using
|
22
|
+
# the Ruby stdlib for a better experience.
|
23
|
+
def local_transport?
|
24
|
+
return false unless defined?(Train::Transports::Local)
|
25
|
+
backend.is_a?(Train::Transports::Local::Connection)
|
26
|
+
end
|
27
|
+
|
20
28
|
# Ruby internal for printing a nice name for this class
|
21
29
|
def to_s
|
22
30
|
'Inspec::Backend::Class'
|
data/lib/inspec/profile.rb
CHANGED
@@ -93,12 +93,20 @@ module Inspec
|
|
93
93
|
@writable = options[:writable] || false
|
94
94
|
@profile_id = options[:id]
|
95
95
|
@cache = options[:cache] || Cache.new
|
96
|
-
@backend = options[:backend] || Inspec::Backend.create(options.select { |k, _| k != 'target' })
|
97
96
|
@attr_values = options[:attributes]
|
98
97
|
@tests_collected = false
|
99
98
|
@libraries_loaded = false
|
100
99
|
Metadata.finalize(@source_reader.metadata, @profile_id, options)
|
101
100
|
|
101
|
+
# if a backend has already been created, clone it so each profile has its own unique backend object
|
102
|
+
# otherwise, create a new backend object
|
103
|
+
#
|
104
|
+
# This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
|
105
|
+
# with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
|
106
|
+
# we share the backend between profiles.
|
107
|
+
#
|
108
|
+
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
109
|
+
@backend = options[:backend].nil? ? Inspec::Backend.create(options) : options[:backend].dup
|
102
110
|
@runtime_profile = RuntimeProfile.new(self)
|
103
111
|
@backend.profile = @runtime_profile
|
104
112
|
|
data/lib/inspec/shell.rb
CHANGED
@@ -139,21 +139,21 @@ EOF
|
|
139
139
|
elsif topic == 'matchers'
|
140
140
|
print_matchers_help
|
141
141
|
elsif !Inspec::Resource.registry[topic].nil?
|
142
|
-
|
143
|
-
#{mark 'Name:'} #{topic}
|
144
|
-
|
145
|
-
#{mark 'Description:'}
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
#{mark 'Example:'}
|
150
|
-
#{print_example(Inspec::Resource.registry[topic].example)}
|
151
|
-
|
152
|
-
#{mark 'Web Reference:'}
|
142
|
+
topic_info = Inspec::Resource.registry[topic]
|
143
|
+
info = "#{mark 'Name:'} #{topic}\n\n"
|
144
|
+
unless topic_info.desc.nil?
|
145
|
+
info += "#{mark 'Description:'}\n\n"
|
146
|
+
info += "#{topic_info.desc}\n\n"
|
147
|
+
end
|
153
148
|
|
154
|
-
|
149
|
+
unless topic_info.example.nil?
|
150
|
+
info += "#{mark 'Example:'}\n"
|
151
|
+
info += "#{print_example(topic_info.example)}\n\n"
|
152
|
+
end
|
155
153
|
|
156
|
-
|
154
|
+
info += "#{mark 'Web Reference:'}\n\n"
|
155
|
+
info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n"
|
156
|
+
puts info
|
157
157
|
else
|
158
158
|
puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
|
159
159
|
end
|
data/lib/inspec/version.rb
CHANGED
data/lib/matchers/matchers.rb
CHANGED
@@ -316,6 +316,8 @@ RSpec::Matchers.define :cmp do |first_expected|
|
|
316
316
|
return actual.to_i.method(op).call(expected)
|
317
317
|
elsif expected.is_a?(Float) && float?(actual)
|
318
318
|
return actual.to_f.method(op).call(expected)
|
319
|
+
elsif actual.is_a?(Symbol) && expected.is_a?(String)
|
320
|
+
return actual.to_s.method(op).call(expected)
|
319
321
|
elsif octal?(expected) && actual.is_a?(Integer)
|
320
322
|
return actual.method(op).call(expected.to_i(8))
|
321
323
|
end
|
data/lib/resources/etc_hosts.rb
CHANGED
@@ -20,7 +20,7 @@ class EtcHosts < Inspec.resource(1)
|
|
20
20
|
include CommentParser
|
21
21
|
|
22
22
|
def initialize(hosts_path = nil)
|
23
|
-
return skip_resource 'The `etc_hosts` resource is not supported on your OS.' unless inspec.os.linux? || inspec.os.windows?
|
23
|
+
return skip_resource 'The `etc_hosts` resource is not supported on your OS.' unless inspec.os.bsd? || inspec.os.linux? || inspec.os.windows?
|
24
24
|
@conf_path = hosts_path || default_hosts_file_path
|
25
25
|
@content = nil
|
26
26
|
@params = nil
|
data/lib/resources/host.rb
CHANGED
data/lib/resources/http.rb
CHANGED
@@ -22,51 +22,201 @@ module Inspec::Resources
|
|
22
22
|
its('Content-Length') { should cmp 258 }
|
23
23
|
its('Content-Type') { should cmp 'text/html; charset=UTF-8' }
|
24
24
|
end
|
25
|
+
|
26
|
+
# properly execute the HTTP call on the scanned machine instead of the
|
27
|
+
# machine executing InSpec. This will be the default behavior in InSpec 2.0.
|
28
|
+
describe http('http://localhost:8080', enable_remote_worker: true) do
|
29
|
+
its('body') { should cmp 'local web server on target machine' }
|
30
|
+
end
|
25
31
|
"
|
26
32
|
|
27
33
|
def initialize(url, opts = {})
|
28
34
|
@url = url
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
@opts = opts
|
36
|
+
|
37
|
+
if use_remote_worker?
|
38
|
+
return skip_resource 'curl is not available on the target machine' unless inspec.command('curl').exist?
|
39
|
+
@worker = Worker::Remote.new(inspec, http_method, url, opts)
|
40
|
+
else
|
41
|
+
@worker = Worker::Local.new(http_method, url, opts)
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
39
45
|
def status
|
40
|
-
|
46
|
+
@worker.status
|
47
|
+
end
|
48
|
+
|
49
|
+
def headers
|
50
|
+
Hashie::Mash.new(@worker.response_headers)
|
41
51
|
end
|
42
52
|
|
43
53
|
def body
|
44
|
-
|
54
|
+
@worker.body
|
45
55
|
end
|
46
56
|
|
47
|
-
def
|
48
|
-
|
57
|
+
def http_method
|
58
|
+
@opts.fetch(:method, 'GET')
|
49
59
|
end
|
50
60
|
|
51
61
|
def to_s
|
52
|
-
"http #{
|
62
|
+
"http #{http_method} on #{@url}"
|
53
63
|
end
|
54
64
|
|
55
65
|
private
|
56
66
|
|
57
|
-
def
|
58
|
-
return
|
59
|
-
|
67
|
+
def use_remote_worker?
|
68
|
+
return false if inspec.local_transport?
|
69
|
+
return true if @opts[:enable_remote_worker]
|
70
|
+
|
71
|
+
warn "[DEPRECATION] #{self} will execute locally instead of the target machine. To execute remotely, add `enable_remote_worker: true`."
|
72
|
+
warn '[DEPRECATION] `enable_remote_worker: true` will be the default behavior in InSpec 2.0.'
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
class Worker
|
77
|
+
class Base
|
78
|
+
attr_reader :http_method, :opts, :url
|
79
|
+
|
80
|
+
def initialize(http_method, url, opts)
|
81
|
+
@http_method = http_method
|
82
|
+
@url = url
|
83
|
+
@opts = opts
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def params
|
89
|
+
opts.fetch(:params, nil)
|
90
|
+
end
|
91
|
+
|
92
|
+
def username
|
93
|
+
opts.fetch(:auth, {})[:user]
|
94
|
+
end
|
95
|
+
|
96
|
+
def password
|
97
|
+
opts.fetch(:auth, {})[:pass]
|
98
|
+
end
|
99
|
+
|
100
|
+
def request_headers
|
101
|
+
opts.fetch(:headers, {})
|
102
|
+
end
|
103
|
+
|
104
|
+
def request_body
|
105
|
+
opts[:data]
|
106
|
+
end
|
107
|
+
|
108
|
+
def open_timeout
|
109
|
+
opts.fetch(:open_timeout, 60)
|
110
|
+
end
|
111
|
+
|
112
|
+
def read_timeout
|
113
|
+
opts.fetch(:read_timeout, 60)
|
114
|
+
end
|
115
|
+
|
116
|
+
def ssl_verify?
|
117
|
+
opts.fetch(:ssl_verify, true)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Local < Base
|
122
|
+
def status
|
123
|
+
response.status
|
124
|
+
end
|
125
|
+
|
126
|
+
def body
|
127
|
+
response.body
|
128
|
+
end
|
129
|
+
|
130
|
+
def response_headers
|
131
|
+
response.headers.to_h
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def response
|
137
|
+
return @response if @response
|
138
|
+
conn = Faraday.new url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }
|
139
|
+
|
140
|
+
# set basic authentication
|
141
|
+
conn.basic_auth username, password unless username.nil? || password.nil?
|
142
|
+
|
143
|
+
# set default timeout
|
144
|
+
conn.options.timeout = read_timeout # open/read timeout in seconds
|
145
|
+
conn.options.open_timeout = open_timeout # connection open timeout in seconds
|
146
|
+
|
147
|
+
@response = conn.send(http_method.downcase) do |req|
|
148
|
+
req.body = request_body
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Remote < Base
|
154
|
+
attr_reader :inspec
|
155
|
+
|
156
|
+
def initialize(inspec, http_method, url, opts)
|
157
|
+
@inspec = inspec
|
158
|
+
super(http_method, url, opts)
|
159
|
+
end
|
160
|
+
|
161
|
+
def status
|
162
|
+
run_curl
|
163
|
+
@status
|
164
|
+
end
|
165
|
+
|
166
|
+
def body
|
167
|
+
run_curl
|
168
|
+
@body
|
169
|
+
end
|
170
|
+
|
171
|
+
def response_headers
|
172
|
+
run_curl
|
173
|
+
@response_headers
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def run_curl
|
179
|
+
return if @ran_curl
|
180
|
+
|
181
|
+
response = inspec.command(curl_command).stdout
|
182
|
+
@ran_curl = true
|
183
|
+
return if response.nil?
|
184
|
+
|
185
|
+
# strip any carriage returns to normalize output
|
186
|
+
response.delete!("\r")
|
187
|
+
|
188
|
+
# split the prelude (status line and headers) and the body
|
189
|
+
prelude, @body = response.split("\n\n", 2)
|
190
|
+
prelude = prelude.lines
|
191
|
+
|
192
|
+
# grab the status off of the first line of the prelude
|
193
|
+
status_line = prelude.shift
|
194
|
+
@status = status_line.split(' ', 3)[1].to_i
|
195
|
+
|
196
|
+
# parse the rest of the prelude which will be all the HTTP headers
|
197
|
+
@response_headers = {}
|
198
|
+
prelude.each do |line|
|
199
|
+
line.strip!
|
200
|
+
key, value = line.split(':', 2)
|
201
|
+
@response_headers[key] = value.strip
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def curl_command
|
206
|
+
cmd = ["curl -i -X #{http_method}"]
|
207
|
+
cmd << "--connect-timeout #{open_timeout}"
|
208
|
+
cmd << "--user \'#{username}:#{password}\'" unless username.nil? || password.nil?
|
209
|
+
cmd << '--insecure' unless ssl_verify?
|
210
|
+
cmd << "--data #{Shellwords.shellescape(request_body)}" unless request_body.nil?
|
60
211
|
|
61
|
-
|
62
|
-
|
212
|
+
request_headers.each do |k, v|
|
213
|
+
cmd << "-H '#{k}=#{v}'"
|
214
|
+
end
|
63
215
|
|
64
|
-
|
65
|
-
conn.options.timeout = @read_timeout # open/read timeout in seconds
|
66
|
-
conn.options.open_timeout = @open_timeout # connection open timeout in seconds
|
216
|
+
cmd << "'#{url}'"
|
67
217
|
|
68
|
-
|
69
|
-
|
218
|
+
cmd.join(' ')
|
219
|
+
end
|
70
220
|
end
|
71
221
|
end
|
72
222
|
end
|
data/lib/resources/processes.rb
CHANGED
@@ -4,9 +4,10 @@
|
|
4
4
|
# author: Christoph Hartmann
|
5
5
|
|
6
6
|
require 'utils/filter'
|
7
|
+
require 'ostruct'
|
7
8
|
|
8
9
|
module Inspec::Resources
|
9
|
-
class Processes < Inspec.resource(1)
|
10
|
+
class Processes < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
|
10
11
|
name 'processes'
|
11
12
|
desc 'Use the processes InSpec audit resource to test properties for programs that are running on the system.'
|
12
13
|
example "
|
@@ -15,12 +16,18 @@ module Inspec::Resources
|
|
15
16
|
its('users') { should eq ['mysql'] }
|
16
17
|
its('states') { should include 'S' }
|
17
18
|
end
|
19
|
+
|
18
20
|
describe processes(/.+/).where { label != 'unconfined' && pid < 1000 } do
|
19
21
|
its('users') { should cmp [] }
|
20
22
|
end
|
23
|
+
|
24
|
+
# work with all processes
|
25
|
+
describe processes do
|
26
|
+
its('entries.length') { should be <= 100 }
|
27
|
+
end
|
21
28
|
"
|
22
29
|
|
23
|
-
def initialize(grep)
|
30
|
+
def initialize(grep = /.*/)
|
24
31
|
@grep = grep
|
25
32
|
# turn into a regexp if it isn't one yet
|
26
33
|
if grep.class == String
|
@@ -80,39 +87,118 @@ module Inspec::Resources
|
|
80
87
|
os = inspec.os
|
81
88
|
|
82
89
|
if os.linux?
|
83
|
-
command
|
84
|
-
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
90
|
+
command, regex, field_map = ps_configuration_for_linux
|
85
91
|
elsif os.windows?
|
86
92
|
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")'
|
87
93
|
# Wanted to use /(?:^|,)([^,]*)/; works on rubular.com not sure why here?
|
88
94
|
regex = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
|
95
|
+
field_map = {
|
96
|
+
pid: 2,
|
97
|
+
cpu: 3,
|
98
|
+
mem: 4,
|
99
|
+
vsz: 5,
|
100
|
+
rss: 6,
|
101
|
+
tty: 7,
|
102
|
+
stat: 8,
|
103
|
+
start: 9,
|
104
|
+
time: 10,
|
105
|
+
user: 11,
|
106
|
+
command: 12,
|
107
|
+
}
|
89
108
|
else
|
90
109
|
command = 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command'
|
91
110
|
regex = /^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
111
|
+
field_map = {
|
112
|
+
pid: 1,
|
113
|
+
cpu: 2,
|
114
|
+
mem: 3,
|
115
|
+
vsz: 4,
|
116
|
+
rss: 5,
|
117
|
+
tty: 6,
|
118
|
+
stat: 7,
|
119
|
+
start: 8,
|
120
|
+
time: 9,
|
121
|
+
user: 10,
|
122
|
+
command: 11,
|
123
|
+
}
|
92
124
|
end
|
93
|
-
build_process_list(command, regex,
|
125
|
+
build_process_list(command, regex, field_map)
|
94
126
|
end
|
95
127
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
128
|
+
def ps_configuration_for_linux
|
129
|
+
if busybox_ps?
|
130
|
+
command = 'ps -o pid,vsz,rss,tty,stat,time,ruser,args'
|
131
|
+
regex = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/
|
132
|
+
field_map = {
|
133
|
+
pid: 1,
|
134
|
+
vsz: 2,
|
135
|
+
rss: 3,
|
136
|
+
tty: 4,
|
137
|
+
stat: 5,
|
138
|
+
time: 6,
|
139
|
+
user: 7,
|
140
|
+
command: 8,
|
141
|
+
}
|
142
|
+
else
|
143
|
+
command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
|
144
|
+
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
145
|
+
field_map = {
|
146
|
+
label: 1,
|
147
|
+
pid: 2,
|
148
|
+
cpu: 3,
|
149
|
+
mem: 4,
|
150
|
+
vsz: 5,
|
151
|
+
rss: 6,
|
152
|
+
tty: 7,
|
153
|
+
stat: 8,
|
154
|
+
start: 9,
|
155
|
+
time: 10,
|
156
|
+
user: 11,
|
157
|
+
command: 12,
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
[command, regex, field_map]
|
162
|
+
end
|
100
163
|
|
101
|
-
def
|
164
|
+
def busybox_ps?
|
165
|
+
@busybox_ps ||= inspec.command('ps --help').stderr.include?('BusyBox')
|
166
|
+
end
|
167
|
+
|
168
|
+
def build_process_list(command, regex, field_map)
|
102
169
|
cmd = inspec.command(command)
|
103
170
|
all = cmd.stdout.split("\n")[1..-1]
|
104
171
|
return [] if all.nil?
|
105
|
-
|
106
|
-
|
172
|
+
|
173
|
+
# map all the process lines into match objects, fetch the available fields,
|
174
|
+
# and then build an OpenStruct of the process data for each process
|
175
|
+
all.map do |line|
|
176
|
+
line = line.match(regex)
|
177
|
+
|
178
|
+
# skip this line if we couldn't match the regular expression
|
179
|
+
next if line.nil?
|
180
|
+
|
181
|
+
# skip this entry if there's no command for this line
|
182
|
+
next if line[field_map[:command]].nil?
|
183
|
+
|
184
|
+
# build a hash of process data that we'll turn into a struct for FilterTable
|
185
|
+
process_data = {}
|
186
|
+
[:label, :pid, :cpu, :mem, :vsz, :rss, :tty, :stat, :start, :time, :user, :command].each do |param|
|
187
|
+
# not all operating systems support all fields, so skip the field if we don't have it
|
188
|
+
process_data[param] = line[field_map[param]] if field_map.key?(param)
|
189
|
+
end
|
190
|
+
|
191
|
+
# ensure pid, vsz, and rss are integers for backward compatibility
|
192
|
+
[:pid, :vsz, :rss].each do |int_param|
|
193
|
+
process_data[int_param] = process_data[int_param].to_i if process_data.key?(int_param)
|
194
|
+
end
|
195
|
+
|
196
|
+
# strip any newlines off the command
|
197
|
+
process_data[:command].strip!
|
198
|
+
|
199
|
+
# return an OpenStruct of the process for future use by FilterTable
|
200
|
+
OpenStruct.new(process_data)
|
107
201
|
end.compact
|
108
|
-
lines.map do |m|
|
109
|
-
a = m.to_a[1..-1] # grab all matching groups
|
110
|
-
a.unshift(nil) unless os.linux? || os.windows?
|
111
|
-
a[1] = a[1].to_i
|
112
|
-
a[4] = a[4].to_i
|
113
|
-
a[5] = a[5].to_i
|
114
|
-
Process.new(*a)
|
115
|
-
end
|
116
202
|
end
|
117
203
|
end
|
118
204
|
end
|