inspec 1.40.0 → 1.41.0

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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -9
  3. data/docs/matchers.md +18 -0
  4. data/docs/plugin_kitchen_inspec.md +18 -24
  5. data/docs/profiles.md +39 -2
  6. data/docs/resources/aide_conf.md.erb +18 -28
  7. data/docs/resources/apache_conf.md.erb +19 -33
  8. data/docs/resources/apt.md.erb +22 -36
  9. data/docs/resources/audit_policy.md.erb +9 -24
  10. data/docs/resources/auditd.md.erb +9 -24
  11. data/docs/resources/auditd_conf.md.erb +20 -34
  12. data/docs/resources/auditd_rules.md.erb +8 -24
  13. data/docs/resources/bash.md.erb +4 -26
  14. data/docs/resources/bond.md.erb +25 -40
  15. data/docs/resources/bridge.md.erb +5 -25
  16. data/docs/resources/bsd_service.md.erb +5 -25
  17. data/docs/resources/command.md.erb +35 -50
  18. data/docs/resources/crontab.md.erb +9 -23
  19. data/docs/resources/csv.md.erb +12 -27
  20. data/docs/resources/dh_params.md +1 -0
  21. data/docs/resources/directory.md.erb +5 -25
  22. data/docs/resources/docker.md.erb +60 -57
  23. data/docs/resources/docker_container.md.erb +23 -19
  24. data/docs/resources/docker_image.md.erb +20 -16
  25. data/docs/resources/etc_fstab.md.erb +5 -2
  26. data/docs/resources/etc_group.md.erb +29 -45
  27. data/docs/resources/etc_hosts.md.erb +6 -0
  28. data/docs/resources/etc_hosts_allow.md.erb +6 -2
  29. data/docs/resources/etc_hosts_deny.md.erb +6 -2
  30. data/docs/resources/file.md.erb +198 -212
  31. data/docs/resources/firewalld.md.erb +7 -1
  32. data/docs/resources/gem.md.erb +21 -35
  33. data/docs/resources/group.md.erb +16 -30
  34. data/docs/resources/grub_conf.md.erb +9 -24
  35. data/docs/resources/host.md.erb +32 -49
  36. data/docs/resources/http.md.erb +38 -44
  37. data/docs/resources/iis_app.md.erb +25 -35
  38. data/docs/resources/iis_site.md.erb +26 -40
  39. data/docs/resources/inetd_conf.md.erb +27 -42
  40. data/docs/resources/ini.md.erb +9 -23
  41. data/docs/resources/interface.md.erb +5 -25
  42. data/docs/resources/iptables.md.erb +15 -29
  43. data/docs/resources/json.md.erb +12 -27
  44. data/docs/resources/kernel_module.md.erb +47 -61
  45. data/docs/resources/kernel_parameter.md.erb +15 -29
  46. data/docs/resources/key_rsa.md.erb +3 -0
  47. data/docs/resources/launchd_service.md.erb +5 -25
  48. data/docs/resources/limits_conf.md.erb +15 -29
  49. data/docs/resources/login_def.md.erb +15 -30
  50. data/docs/resources/mount.md.erb +18 -33
  51. data/docs/resources/mssql_session.md.erb +9 -12
  52. data/docs/resources/mysql_conf.md.erb +17 -32
  53. data/docs/resources/mysql_session.md.erb +15 -29
  54. data/docs/resources/nginx.md.erb +6 -0
  55. data/docs/resources/nginx_conf.md.erb +25 -20
  56. data/docs/resources/npm.md.erb +19 -35
  57. data/docs/resources/ntp_conf.md.erb +20 -37
  58. data/docs/resources/oneget.md.erb +15 -30
  59. data/docs/resources/oracledb_session.md.erb +9 -11
  60. data/docs/resources/os.md.erb +29 -43
  61. data/docs/resources/os_env.md.erb +29 -44
  62. data/docs/resources/package.md.erb +33 -42
  63. data/docs/resources/parse_config.md.erb +5 -25
  64. data/docs/resources/parse_config_file.md.erb +31 -43
  65. data/docs/resources/passwd.md.erb +24 -39
  66. data/docs/resources/pip.md.erb +20 -35
  67. data/docs/resources/port.md.erb +43 -57
  68. data/docs/resources/postgres_conf.md.erb +17 -31
  69. data/docs/resources/postgres_hba_conf.md.erb +26 -38
  70. data/docs/resources/postgres_ident_conf.md.erb +25 -37
  71. data/docs/resources/postgres_session.md.erb +15 -29
  72. data/docs/resources/powershell.md.erb +27 -42
  73. data/docs/resources/processes.md.erb +17 -33
  74. data/docs/resources/rabbitmq_config.md.erb +9 -24
  75. data/docs/resources/registry_key.md.erb +27 -42
  76. data/docs/resources/runit_service.md.erb +5 -25
  77. data/docs/resources/security_policy.md.erb +12 -27
  78. data/docs/resources/service.md.erb +27 -42
  79. data/docs/resources/shadow.md.erb +20 -35
  80. data/docs/resources/ssh_config.md.erb +19 -34
  81. data/docs/resources/sshd_config.md.erb +19 -34
  82. data/docs/resources/ssl.md.erb +39 -54
  83. data/docs/resources/sys_info.md.erb +12 -26
  84. data/docs/resources/systemd_service.md.erb +5 -25
  85. data/docs/resources/sysv_service.md.erb +5 -25
  86. data/docs/resources/upstart_service.md.erb +5 -25
  87. data/docs/resources/user.md.erb +29 -44
  88. data/docs/resources/users.md.erb +12 -26
  89. data/docs/resources/vbscript.md.erb +9 -24
  90. data/docs/resources/virtualization.md.erb +8 -23
  91. data/docs/resources/windows_feature.md.erb +15 -30
  92. data/docs/resources/windows_hotfix.md.erb +15 -9
  93. data/docs/resources/windows_task.md.erb +12 -26
  94. data/docs/resources/wmi.md.erb +9 -24
  95. data/docs/resources/x509_certificate.md.erb +4 -0
  96. data/docs/resources/xinetd_conf.md.erb +65 -80
  97. data/docs/resources/xml.md.erb +12 -26
  98. data/docs/resources/yaml.md.erb +12 -27
  99. data/docs/resources/yum.md.erb +37 -51
  100. data/docs/resources/zfs_dataset.md.erb +15 -26
  101. data/docs/resources/zfs_pool.md.erb +9 -20
  102. data/lib/inspec/backend.rb +8 -0
  103. data/lib/inspec/profile.rb +9 -1
  104. data/lib/inspec/shell.rb +13 -13
  105. data/lib/inspec/version.rb +1 -1
  106. data/lib/matchers/matchers.rb +2 -0
  107. data/lib/resources/etc_hosts.rb +1 -1
  108. data/lib/resources/host.rb +4 -1
  109. data/lib/resources/http.rb +173 -23
  110. data/lib/resources/processes.rb +106 -20
  111. data/lib/resources/ssh_conf.rb +1 -1
  112. data/lib/resources/ssl.rb +4 -3
  113. data/lib/utils/object_traversal.rb +35 -10
  114. 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/).
@@ -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'
@@ -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
 
@@ -139,21 +139,21 @@ EOF
139
139
  elsif topic == 'matchers'
140
140
  print_matchers_help
141
141
  elsif !Inspec::Resource.registry[topic].nil?
142
- puts <<EOF
143
- #{mark 'Name:'} #{topic}
144
-
145
- #{mark 'Description:'}
146
-
147
- #{Inspec::Resource.registry[topic].desc}
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
- https://www.inspec.io/docs/reference/resources/#{topic}
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
- EOF
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
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.40.0'.freeze
7
+ VERSION = '1.41.0'.freeze
8
8
  end
@@ -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
@@ -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
@@ -113,7 +113,10 @@ module Inspec::Resources
113
113
  end
114
114
 
115
115
  def to_s
116
- "Host #{hostname}"
116
+ resource_name = "Host #{hostname}"
117
+ resource_name += " port #{port} proto #{protocol}" if port
118
+
119
+ resource_name
117
120
  end
118
121
 
119
122
  private
@@ -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
- @method = opts.fetch(:method, 'GET')
30
- @params = opts.fetch(:params, nil)
31
- @auth = opts.fetch(:auth, {})
32
- @headers = opts.fetch(:headers, {})
33
- @data = opts.fetch(:data, nil)
34
- @open_timeout = opts.fetch(:open_timeout, 60)
35
- @read_timeout = opts.fetch(:read_timeout, 60)
36
- @ssl_verify = opts.fetch(:ssl_verify, true)
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
- response.status
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
- response.body
54
+ @worker.body
45
55
  end
46
56
 
47
- def headers
48
- Hashie::Mash.new(response.headers.to_h)
57
+ def http_method
58
+ @opts.fetch(:method, 'GET')
49
59
  end
50
60
 
51
61
  def to_s
52
- "http #{@method} on #{@url}"
62
+ "http #{http_method} on #{@url}"
53
63
  end
54
64
 
55
65
  private
56
66
 
57
- def response
58
- return @response if @response
59
- conn = Faraday.new url: @url, headers: @headers, params: @params, ssl: { verify: @ssl_verify }
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
- # set basic authentication
62
- conn.basic_auth @auth[:user], @auth[:pass] unless @auth.empty?
212
+ request_headers.each do |k, v|
213
+ cmd << "-H '#{k}=#{v}'"
214
+ end
63
215
 
64
- # set default timeout
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
- @response = conn.send(@method.downcase) do |req|
69
- req.body = @data
218
+ cmd.join(' ')
219
+ end
70
220
  end
71
221
  end
72
222
  end
@@ -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 = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,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, os)
125
+ build_process_list(command, regex, field_map)
94
126
  end
95
127
 
96
- Process = Struct.new(:label, :pid,
97
- :cpu, :mem, :vsz,
98
- :rss, :tty, :stat,
99
- :start, :time, :user, :command)
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 build_process_list(command, regex, os)
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
- lines = all.map do |line|
106
- line.match(regex)
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