inspec-core 4.18.51 → 4.18.85

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +61 -0
  3. data/README.md +3 -3
  4. data/inspec-core.gemspec +51 -0
  5. data/lib/bundles/inspec-supermarket/cli.rb +1 -0
  6. data/lib/inspec/backend.rb +49 -47
  7. data/lib/inspec/base_cli.rb +2 -2
  8. data/lib/inspec/cached_fetcher.rb +4 -0
  9. data/lib/inspec/cli.rb +5 -0
  10. data/lib/inspec/config.rb +1 -1
  11. data/lib/inspec/control_eval_context.rb +131 -199
  12. data/lib/inspec/dependencies/requirement.rb +1 -1
  13. data/lib/inspec/dependencies/resolver.rb +46 -0
  14. data/lib/inspec/dsl_shared.rb +25 -3
  15. data/lib/inspec/fetcher.rb +0 -3
  16. data/lib/inspec/fetcher/git.rb +4 -0
  17. data/lib/inspec/fetcher/url.rb +1 -2
  18. data/lib/inspec/file_provider.rb +4 -2
  19. data/lib/inspec/library_eval_context.rb +37 -37
  20. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +27 -0
  21. data/lib/inspec/plugin/v1/plugins.rb +0 -1
  22. data/lib/inspec/profile.rb +8 -6
  23. data/lib/inspec/profile_context.rb +74 -9
  24. data/lib/inspec/profile_vendor.rb +48 -3
  25. data/lib/inspec/resource.rb +192 -41
  26. data/lib/inspec/resources/aide_conf.rb +1 -1
  27. data/lib/inspec/resources/apache_conf.rb +15 -31
  28. data/lib/inspec/resources/command.rb +1 -1
  29. data/lib/inspec/resources/crontab.rb +56 -56
  30. data/lib/inspec/resources/etc_fstab.rb +1 -1
  31. data/lib/inspec/resources/etc_group.rb +1 -1
  32. data/lib/inspec/resources/etc_hosts.rb +2 -3
  33. data/lib/inspec/resources/etc_hosts_allow_deny.rb +1 -1
  34. data/lib/inspec/resources/file.rb +2 -2
  35. data/lib/inspec/resources/filesystem.rb +4 -4
  36. data/lib/inspec/resources/groups.rb +16 -2
  37. data/lib/inspec/resources/iis_app.rb +1 -1
  38. data/lib/inspec/resources/ini.rb +1 -2
  39. data/lib/inspec/resources/mount.rb +2 -2
  40. data/lib/inspec/resources/oracledb_session.rb +1 -1
  41. data/lib/inspec/resources/package.rb +22 -0
  42. data/lib/inspec/resources/passwd.rb +1 -1
  43. data/lib/inspec/resources/platform.rb +36 -36
  44. data/lib/inspec/resources/port.rb +1 -1
  45. data/lib/inspec/resources/postfix_conf.rb +1 -1
  46. data/lib/inspec/resources/service.rb +23 -15
  47. data/lib/inspec/resources/users.rb +3 -3
  48. data/lib/inspec/resources/virtualization.rb +15 -11
  49. data/lib/inspec/resources/x509_certificate.rb +18 -4
  50. data/lib/inspec/resources/xinetd_conf.rb +1 -1
  51. data/lib/inspec/resources/xml.rb +1 -2
  52. data/lib/inspec/rspec_extensions.rb +12 -0
  53. data/lib/inspec/rule.rb +63 -22
  54. data/lib/inspec/utils/filter.rb +2 -0
  55. data/lib/inspec/utils/parser.rb +244 -240
  56. data/lib/inspec/utils/simpleconfig.rb +1 -1
  57. data/lib/inspec/version.rb +1 -1
  58. data/lib/matchers/matchers.rb +11 -10
  59. data/lib/plugins/inspec-compliance/lib/inspec-compliance.rb +3 -0
  60. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +2 -2
  61. data/lib/plugins/inspec-init/templates/profiles/aws/README.md +192 -0
  62. data/lib/plugins/inspec-init/templates/profiles/aws/attributes.yml +2 -0
  63. data/lib/plugins/inspec-init/templates/profiles/aws/controls/example.rb +39 -0
  64. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +22 -0
  65. data/lib/plugins/inspec-init/templates/profiles/azure/README.md +56 -0
  66. data/lib/plugins/inspec-init/templates/profiles/azure/controls/example.rb +14 -0
  67. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +14 -0
  68. data/lib/plugins/inspec-init/templates/profiles/gcp/README.md +66 -0
  69. data/lib/plugins/inspec-init/templates/profiles/gcp/attributes.yml +2 -0
  70. data/lib/plugins/inspec-init/templates/profiles/gcp/controls/example.rb +27 -0
  71. data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +19 -0
  72. data/lib/source_readers/inspec.rb +1 -1
  73. metadata +87 -74
  74. data/lib/inspec/plugin/v1/plugin_types/resource.rb +0 -176
  75. data/lib/plugins/inspec-init/templates/profiles/os/libraries/.gitkeep +0 -0
@@ -67,7 +67,7 @@ module Inspec::Resources
67
67
  res = if inspec.platform.name == "alpine"
68
68
  inspec.backend.run_command("which \"#{@command}\"")
69
69
  else
70
- inspec.backend.run_command("bash -c 'type \"#{@command}\"'")
70
+ inspec.backend.run_command("sh -c 'type \"#{@command}\"'")
71
71
  end
72
72
  elsif inspec.os.windows?
73
73
  res = inspec.backend.run_command("Get-Command \"#{@command}\"")
@@ -32,7 +32,7 @@ module Inspec::Resources
32
32
 
33
33
  attr_reader :params
34
34
 
35
- include CommentParser
35
+ include Inspec::Utils::CommentParser
36
36
 
37
37
  def initialize(opts = nil)
38
38
  if opts.respond_to?(:fetch)
@@ -67,6 +67,7 @@ module Inspec::Resources
67
67
  end
68
68
 
69
69
  def crontab_cmd
70
+ # TODO: the -u scenario needs to be able to do sudo
70
71
  @user.nil? ? "crontab -l" : "crontab -l -u #{@user}"
71
72
  end
72
73
 
@@ -108,66 +109,65 @@ module Inspec::Resources
108
109
  !@user.nil?
109
110
  end
110
111
 
111
- def parse_system_crontab(data)
112
+ # rubocop:disable Layout/AlignHash
113
+
114
+ DEFAULT_TIMES = {
115
+ "minute" => "*",
116
+ "hour" => "*",
117
+ "day" => "*",
118
+ "month" => "*",
119
+ "weekday" => "*",
120
+ }.freeze
121
+
122
+ SYSTEM_COLUMNS = %w{minute hour day month weekday user command}.freeze
123
+ USER_COLUMNS = %w{minute hour day month weekday command}.freeze
124
+
125
+ HOURLY = { "minute" => "0" }.freeze
126
+ DAILY = HOURLY .merge("hour" => "0").freeze
127
+ WEEKLY = HOURLY .merge("weekday" => "0").freeze
128
+ MONTHLY = DAILY .merge("day" => "1").freeze
129
+ YEARLY = MONTHLY .merge("month" => "1").freeze
130
+ REBOOT = {
131
+ "minute" => "-1",
132
+ "hour" => "-1",
133
+ "day" => "-1",
134
+ "month" => "-1",
135
+ "weekday" => "-1",
136
+ }.freeze
137
+
138
+ def merge_crontab(data, default)
112
139
  case data
113
- when /@hourly .*/
114
- elements = data.split(/\s+/, 3)
115
- { "minute" => "0", "hour" => "*", "day" => "*", "month" => "*", "weekday" => "*", "user" => elements.at(1), "command" => elements.at(2) }
116
- when /@(midnight|daily) .*/
117
- elements = data.split(/\s+/, 3)
118
- { "minute" => "0", "hour" => "0", "day" => "*", "month" => "*", "weekday" => "*", "user" => elements.at(1), "command" => elements.at(2) }
119
- when /@weekly .*/
120
- elements = data.split(/\s+/, 3)
121
- { "minute" => "0", "hour" => "0", "day" => "*", "month" => "*", "weekday" => "0", "user" => elements.at(1), "command" => elements.at(2) }
122
- when /@monthly ./
123
- elements = data.split(/\s+/, 3)
124
- { "minute" => "0", "hour" => "0", "day" => "1", "month" => "*", "weekday" => "*", "user" => elements.at(1), "command" => elements.at(2) }
125
- when /@(annually|yearly) .*/
126
- elements = data.split(/\s+/, 3)
127
- { "minute" => "0", "hour" => "0", "day" => "1", "month" => "1", "weekday" => "*", "user" => elements.at(1), "command" => elements.at(2) }
128
- when /@reboot .*/
129
- elements = data.split(/\s+/, 3)
130
- { "minute" => "-1", "hour" => "-1", "day" => "-1", "month" => "-1", "weekday" => "-1", "user" => elements.at(1), "command" => elements.at(2) }
131
- else
132
- elements = data.split(/\s+/, 7)
133
- {
134
- "minute" => elements.at(0),
135
- "hour" => elements.at(1),
136
- "day" => elements.at(2),
137
- "month" => elements.at(3),
138
- "weekday" => elements.at(4),
139
- "user" => elements.at(5),
140
- "command" => elements.at(6),
141
- }
140
+ when /@hourly /
141
+ default.merge(HOURLY)
142
+ when /@(midnight|daily) /
143
+ default.merge(DAILY)
144
+ when /@weekly /
145
+ default.merge(WEEKLY)
146
+ when /@monthly /
147
+ default.merge(MONTHLY)
148
+ when /@(annually|yearly) /
149
+ default.merge(YEARLY)
150
+ when /@reboot /
151
+ default.merge(REBOOT)
142
152
  end
143
153
  end
144
154
 
155
+ def parse_system_crontab(data)
156
+ _, user, cmd = data.split(/\s+/, 3)
157
+ default = DEFAULT_TIMES.merge("user" => user,
158
+ "command" => cmd)
159
+
160
+ merge_crontab(data, default) ||
161
+ SYSTEM_COLUMNS.zip(data.split(/\s+/, 7)).to_h
162
+ end
163
+
145
164
  def parse_user_crontab(data)
146
- case data
147
- when /@hourly .*/
148
- { "minute" => "0", "hour" => "*", "day" => "*", "month" => "*", "weekday" => "*", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
149
- when /@(midnight|daily) .*/
150
- { "minute" => "0", "hour" => "0", "day" => "*", "month" => "*", "weekday" => "*", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
151
- when /@weekly .*/
152
- { "minute" => "0", "hour" => "0", "day" => "*", "month" => "*", "weekday" => "0", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
153
- when /@monthly ./
154
- { "minute" => "0", "hour" => "0", "day" => "1", "month" => "*", "weekday" => "*", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
155
- when /@(annually|yearly) .*/
156
- { "minute" => "0", "hour" => "0", "day" => "1", "month" => "1", "weekday" => "*", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
157
- when /@reboot .*/
158
- { "minute" => "-1", "hour" => "-1", "day" => "-1", "month" => "-1", "weekday" => "-1", "user" => @user, "command" => data.split(/\s+/, 2).at(1) }
159
- else
160
- elements = data.split(/\s+/, 6)
161
- {
162
- "minute" => elements.at(0),
163
- "hour" => elements.at(1),
164
- "day" => elements.at(2),
165
- "month" => elements.at(3),
166
- "weekday" => elements.at(4),
167
- "user" => @user,
168
- "command" => elements.at(5),
169
- }
170
- end
165
+ _, cmd = data.split(/\s+/, 2)
166
+ default = DEFAULT_TIMES.merge("user" => @user,
167
+ "command" => cmd)
168
+
169
+ merge_crontab(data, default) ||
170
+ USER_COLUMNS.zip(data.split(/\s+/, 6)).to_h.merge("user" => @user)
171
171
  end
172
172
  end
173
173
  end
@@ -25,7 +25,7 @@ module Inspec::Resources
25
25
 
26
26
  attr_reader :params
27
27
 
28
- include CommentParser
28
+ include Inspec::Utils::CommentParser
29
29
  include FileReader
30
30
 
31
31
  def initialize(fstab_path = nil)
@@ -24,7 +24,7 @@ require "inspec/utils/file_reader"
24
24
  module Inspec::Resources
25
25
  class EtcGroup < Inspec.resource(1)
26
26
  include Converter
27
- include CommentParser
27
+ include Inspec::Utils::CommentParser
28
28
 
29
29
  name "etc_group"
30
30
  supports platform: "unix"
@@ -4,8 +4,7 @@ require "inspec/utils/file_reader"
4
4
  module Inspec::Resources
5
5
  class EtcHosts < Inspec.resource(1)
6
6
  name "etc_hosts"
7
- supports platform: "linux"
8
- supports platform: "bsd"
7
+ supports platform: "unix"
9
8
  supports platform: "windows"
10
9
  desc 'Use the etc_hosts InSpec audit resource to find an
11
10
  ip_address and its associated hosts'
@@ -19,7 +18,7 @@ module Inspec::Resources
19
18
 
20
19
  attr_reader :params
21
20
 
22
- include CommentParser
21
+ include Inspec::Utils::CommentParser
23
22
  include FileReader
24
23
 
25
24
  DEFAULT_UNIX_PATH = "/etc/hosts".freeze
@@ -16,7 +16,7 @@ module Inspec::Resources
16
16
 
17
17
  attr_reader :params
18
18
 
19
- include CommentParser
19
+ include Inspec::Utils::CommentParser
20
20
  include FileReader
21
21
 
22
22
  def initialize(hosts_allow_path = nil)
@@ -17,12 +17,12 @@ module Inspec::Resources
17
17
  # TODO: rename file_resource.rb
18
18
  class FileResource < Inspec.resource(1)
19
19
  include FilePermissionsSelector
20
- include LinuxMountParser
20
+ include Inspec::Utils::LinuxMountParser
21
21
 
22
22
  name "file"
23
23
  supports platform: "unix"
24
24
  supports platform: "windows"
25
- desc "Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, named pipes, sockets, character devices, block devices, and doors."
25
+ desc "Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, etc."
26
26
  example <<~EXAMPLE
27
27
  describe file('path') do
28
28
  it { should exist }
@@ -3,7 +3,7 @@ require "inspec/resources/command"
3
3
  module Inspec::Resources
4
4
  class FileSystemResource < Inspec.resource(1)
5
5
  name "filesystem"
6
- supports platform: "linux"
6
+ supports platform: "unix"
7
7
  supports platform: "windows"
8
8
  desc "Use the filesystem InSpec resource to test file system"
9
9
  example <<~EXAMPLE
@@ -29,8 +29,8 @@ module Inspec::Resources
29
29
  @fsman = nil
30
30
 
31
31
  os = inspec.os
32
- if os.linux?
33
- @fsman = LinuxFileSystemResource.new(inspec)
32
+ if os.unix?
33
+ @fsman = UnixFileSystemResource.new(inspec)
34
34
  elsif os.windows?
35
35
  @fsman = WindowsFileSystemResource.new(inspec)
36
36
  else
@@ -93,7 +93,7 @@ module Inspec::Resources
93
93
  end
94
94
  end
95
95
 
96
- class LinuxFileSystemResource < FsManagement
96
+ class UnixFileSystemResource < FsManagement
97
97
  def info(partition)
98
98
  cmd = inspec.command("df #{partition} -PT")
99
99
  if cmd.stdout.nil? || cmd.stdout.empty? || cmd.exit_status != 0
@@ -173,6 +173,8 @@ module Inspec::Resources
173
173
  end
174
174
 
175
175
  def groups
176
+ # https://apple.stackexchange.com/a/130815
177
+
176
178
  group_by_id = runmap("dscl . -list /Groups PrimaryGroupID") { |l| name, id = l.split; [id.to_i, name] }
177
179
  userss = runmap("dscl . -list /Users PrimaryGroupID") { |l| name, id = l.split; [name, id.to_i] }
178
180
  membership = runmap("dscl . -list /Groups GroupMembership") { |l| key, *vs = l.split; [key, vs] }
@@ -196,9 +198,21 @@ module Inspec::Resources
196
198
  users = g.delete("users") || ""
197
199
  users = users.split
198
200
  users += Array(users_by_group[g["name"]])
199
- g["members"] = users
200
- g["members"].sort.join ","
201
+ g["members"] = users.sort
201
202
  end
203
+
204
+ groups # de-dupe/merge by gid
205
+ .group_by { |g| g["gid"] }
206
+ .values
207
+ .map { |subgroups|
208
+ g = subgroups.first
209
+
210
+ if subgroups.size != 1
211
+ g["members"] = subgroups.map { |h| h["members"] }.flatten.uniq
212
+ end
213
+
214
+ g
215
+ }
202
216
  end
203
217
  end
204
218
 
@@ -10,7 +10,7 @@ module Inspec::Resources
10
10
  describe iis_app('/myapp', 'Default Web Site') do
11
11
  it { should exist }
12
12
  it { should have_application_pool('MyAppPool') }
13
- it { should have_protocols('http') }
13
+ it { should have_protocol('http') }
14
14
  it { should have_site_name('Default Web Site') }
15
15
  it { should have_physical_path('C:\\inetpub\\wwwroot\\myapp') }
16
16
  it { should have_path('\\My Application') }
@@ -4,8 +4,7 @@ require "inspec/utils/simpleconfig"
4
4
  module Inspec::Resources
5
5
  class IniConfig < JsonConfig
6
6
  name "ini"
7
- supports platform: "unix"
8
- supports platform: "windows"
7
+ supports platform: "os"
9
8
  desc "Use the ini InSpec audit resource to test data in a INI file."
10
9
  example <<~EXAMPLE
11
10
  descibe ini do
@@ -79,10 +79,10 @@ module Inspec::Resources
79
79
  end
80
80
 
81
81
  class LinuxMounts < MountsInfo
82
- include LinuxMountParser
82
+ include Inspec::Utils::LinuxMountParser
83
83
  end
84
84
 
85
85
  class BsdMounts < MountsInfo
86
- include BsdMountParser
86
+ include Inspec::Utils::BsdMountParser
87
87
  end
88
88
  end
@@ -86,7 +86,7 @@ module Inspec::Resources
86
86
  elsif @su_user.nil?
87
87
  %{#{sql_prefix}#{bin} "#{user}"/"#{password}"@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
88
88
  else
89
- %{su - #{@su_user} -c "env ORACLE_SID=#{@service} #{bin} / as #{@db_role}#{sql_postfix}}
89
+ %{su - #{@su_user} -c "env ORACLE_SID=#{@service} #{bin} / as #{@db_role}#{sql_postfix}"}
90
90
  end
91
91
  end
92
92
 
@@ -46,6 +46,8 @@ module Inspec::Resources
46
46
  @pkgman = HpuxPkg.new(inspec)
47
47
  elsif ["alpine"].include?(os[:name])
48
48
  @pkgman = AlpinePkg.new(inspec)
49
+ elsif ["freebsd"].include?(os[:name])
50
+ @pkgman = FreebsdPkg.new(inspec)
49
51
  else
50
52
  raise Inspec::Exceptions::ResourceSkipped, "The `package` resource is not supported on your OS yet."
51
53
  end
@@ -274,6 +276,26 @@ module Inspec::Resources
274
276
  end
275
277
  end
276
278
 
279
+ class FreebsdPkg < PkgManagement
280
+ def info(package_name)
281
+ cmd = inspec.command("pkg info #{package_name}")
282
+ return {} if cmd.exit_status.to_i != 0
283
+
284
+ params = SimpleConfig.new(
285
+ cmd.stdout.chomp,
286
+ assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
287
+ multiple_values: false
288
+ ).params
289
+
290
+ {
291
+ name: params["Name"],
292
+ installed: true,
293
+ version: params["Version"],
294
+ type: "pkg",
295
+ }
296
+ end
297
+ end
298
+
277
299
  # Determines the installed packages on Windows using the Windows package registry entries.
278
300
  # @see: http://blogs.technet.com/b/heyscriptingguy/archive/2013/11/15/use-powershell-to-find-installed-software.aspx
279
301
  class WindowsPkg < PkgManagement
@@ -33,7 +33,7 @@ module Inspec::Resources
33
33
  end
34
34
  EXAMPLE
35
35
 
36
- include PasswdParser
36
+ include Inspec::Utils::PasswdParser
37
37
  include FileReader
38
38
 
39
39
  attr_reader :params
@@ -18,11 +18,16 @@ module Inspec::Resources
18
18
  @platform = inspec.backend.platform
19
19
  end
20
20
 
21
- # add helper methods for easy access of properties
22
- %w{family release arch}.each do |property|
23
- define_method(property.to_sym) do
24
- @platform[property]
25
- end
21
+ def family
22
+ @platform.family
23
+ end
24
+
25
+ def release
26
+ @platform.release
27
+ end
28
+
29
+ def arch
30
+ @platform.arch if @platform.respond_to?(:arch)
26
31
  end
27
32
 
28
33
  def families
@@ -51,46 +56,41 @@ module Inspec::Resources
51
56
  end
52
57
 
53
58
  def params
59
+ # rubocop:disable Layout/AlignHash
54
60
  h = {
55
- name: name,
61
+ name: name,
56
62
  families: families,
57
- release: release,
63
+ release: release,
64
+ arch: arch,
58
65
  }
66
+ # rubocop:enable Layout/AlignHash
59
67
 
60
- # Avoid adding Arch for APIs (not applicable)
61
- unless in_family?("api")
62
- h[:arch] = arch
63
- end
68
+ h.delete :arch if in_family?("api") # not applicable if api
64
69
 
65
70
  h
66
71
  end
67
72
 
68
73
  def supported?(supports)
69
- return true if supports.nil? || supports.empty?
70
-
71
- status = true
72
- supports.each do |support|
73
- support.each do |k, v|
74
- status =
75
- case k
76
- when :os_family, :"os-family", :platform_family, :"platform-family" then
77
- in_family?(v)
78
- when :os, :platform then
79
- platform?(v)
80
- when :os_name, :"os-name", :platform_name, :"platform-name" then
81
- name == v
82
- when :release then
83
- check_release(v)
84
- else
85
- false
86
- end
87
-
88
- break if status == false
89
- end
90
- return true if status == true
91
- end
74
+ raise ArgumentError, "`supports` is nil." unless supports
75
+
76
+ supports.any? { |support|
77
+ support.all? { |k, v|
78
+ case normalize(k)
79
+ when :os_family, :platform_family then
80
+ in_family?(v)
81
+ when :os, :platform then
82
+ platform?(v)
83
+ when :os_name, :platform_name then
84
+ name == v
85
+ when :release then
86
+ check_release(v)
87
+ end
88
+ }
89
+ } || supports.empty?
90
+ end
92
91
 
93
- status
92
+ def normalize(key) # TODO: dumb... push this up or remove need
93
+ key.to_s.tr("-", "_").to_sym
94
94
  end
95
95
 
96
96
  def to_s
@@ -103,7 +103,7 @@ module Inspec::Resources
103
103
  # allow wild card matching
104
104
  if value.include?("*")
105
105
  cleaned = Regexp.escape(value).gsub('\*', ".*?")
106
- !(release =~ /#{cleaned}/).nil?
106
+ release =~ /#{cleaned}/
107
107
  else
108
108
  release == value
109
109
  end