inspec 1.40.0 → 1.41.0

Sign up to get free protection for your applications and to get access to all the features.
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