inspec-core 4.56.58 → 5.7.9

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +24 -9
  3. data/etc/deprecations.json +12 -11
  4. data/inspec-core.gemspec +3 -5
  5. data/lib/inspec/base_cli.rb +14 -2
  6. data/lib/inspec/cli.rb +16 -7
  7. data/lib/inspec/dependencies/dependency_set.rb +2 -6
  8. data/lib/inspec/dependency_installer.rb +74 -0
  9. data/lib/inspec/dependency_loader.rb +97 -0
  10. data/lib/inspec/dsl.rb +16 -23
  11. data/lib/inspec/env_printer.rb +1 -1
  12. data/lib/inspec/errors.rb +7 -0
  13. data/lib/inspec/fetcher/git.rb +28 -43
  14. data/lib/inspec/fetcher/url.rb +1 -1
  15. data/lib/inspec/formatters/base.rb +22 -0
  16. data/lib/inspec/metadata.rb +36 -0
  17. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
  18. data/lib/inspec/profile.rb +81 -29
  19. data/lib/inspec/reporters/automate.rb +1 -1
  20. data/lib/inspec/reporters/cli.rb +1 -1
  21. data/lib/inspec/reporters/json.rb +31 -11
  22. data/lib/inspec/resource.rb +6 -0
  23. data/lib/inspec/resources/cassandradb_session.rb +3 -4
  24. data/lib/inspec/resources/cron.rb +49 -0
  25. data/lib/inspec/resources/file.rb +1 -1
  26. data/lib/inspec/resources/ibmdb2_session.rb +3 -4
  27. data/lib/inspec/resources/ipfilter.rb +59 -0
  28. data/lib/inspec/resources/ipnat.rb +58 -0
  29. data/lib/inspec/resources/mongodb_session.rb +1 -7
  30. data/lib/inspec/resources/oracledb_session.rb +7 -20
  31. data/lib/inspec/resources/postgres_session.rb +5 -7
  32. data/lib/inspec/resources/processes.rb +4 -6
  33. data/lib/inspec/resources/service.rb +2 -4
  34. data/lib/inspec/resources.rb +3 -16
  35. data/lib/inspec/rule.rb +1 -1
  36. data/lib/inspec/runner.rb +18 -1
  37. data/lib/inspec/runner_rspec.rb +15 -0
  38. data/lib/inspec/schema/exec_json.rb +59 -58
  39. data/lib/inspec/schema/exec_json_min.rb +16 -16
  40. data/lib/inspec/schema/primitives.rb +68 -51
  41. data/lib/inspec/schema/profile_json.rb +27 -27
  42. data/lib/inspec/schema.rb +1 -0
  43. data/lib/inspec/secrets/yaml.rb +1 -7
  44. data/lib/inspec/ui.rb +1 -0
  45. data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
  46. data/lib/inspec/version.rb +1 -1
  47. data/lib/inspec.rb +3 -0
  48. data/lib/matchers/matchers.rb +1 -7
  49. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  50. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
  51. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
  52. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
  53. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
  54. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
  55. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
  56. data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
  57. data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
  58. data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
  59. data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
  60. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +6 -4
  61. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +4 -1
  62. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +2 -1
  63. data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
  65. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
  66. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
  67. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
  68. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
  69. metadata +24 -7
@@ -1,11 +1,10 @@
1
1
  module Inspec::Resources
2
2
  class Lines
3
- attr_reader :output, :exit_status
3
+ attr_reader :output
4
4
 
5
- def initialize(raw, desc, exit_status)
5
+ def initialize(raw, desc)
6
6
  @output = raw
7
7
  @desc = desc
8
- @exit_status = exit_status
9
8
  end
10
9
 
11
10
  def to_s
@@ -59,7 +58,7 @@ module Inspec::Resources
59
58
  if cmd.exit_status != 0 || out =~ /Can't connect to IBM Db2 / || out.downcase =~ /^error:.*/
60
59
  raise Inspec::Exceptions::ResourceFailed, "IBM Db2 connection error: #{out}"
61
60
  else
62
- Lines.new(cmd.stdout.strip, "IBM Db2 Query: #{q}", cmd.exit_status)
61
+ Lines.new(cmd.stdout.strip, "IBM Db2 Query: #{q}")
63
62
  end
64
63
  end
65
64
 
@@ -0,0 +1,59 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class IpFilter < Inspec.resource(1)
4
+ name "ipfilter"
5
+ supports platform: "bsd"
6
+ supports platform: "solaris"
7
+ desc "Use the ipfilter InSpec audit resource to test rules that are defined for ipfilter, which maintains the IP rule set"
8
+ example <<~EXAMPLE
9
+ describe ipfilter do
10
+ it { should have_rule("pass in quick on lo0 all") }
11
+ end
12
+ EXAMPLE
13
+
14
+ def initialize
15
+ # checks if the instance is either bsd or solaris
16
+ return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
17
+
18
+ # ensures, all calls are aborted for non-supported os
19
+ @ipfilter_cache = []
20
+ skip_resource "The `ipfilter` resource is not supported on your OS yet."
21
+ end
22
+
23
+ def has_rule?(rule = nil)
24
+ # checks if the rule is part of the ruleset
25
+ retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
26
+ end
27
+
28
+ def retrieve_rules
29
+ # this would be true if the OS family was not bsd/solaris when checked in initliaze
30
+ return @ipfilter_cache if defined?(@ipfilter_cache)
31
+
32
+ # construct ipfstat command to read all rules
33
+ bin = find_ipfstat_or_error
34
+ ipfstat_cmd = "#{bin} -io"
35
+ cmd = inspec.command(ipfstat_cmd)
36
+
37
+ # Return empty array when command is not executed successfully
38
+ # or there is no output since no rules are active
39
+ return [] if cmd.exit_status.to_i != 0 || cmd.stdout == ""
40
+
41
+ # split rules, returns array or rules
42
+ @ipfilter_cache = cmd.stdout.split("\n").map(&:strip)
43
+ end
44
+
45
+ def to_s
46
+ "Ipfilter"
47
+ end
48
+
49
+ private
50
+
51
+ def find_ipfstat_or_error
52
+ %w{/usr/sbin/ipfstat /sbin/ipfstat ipfstat}.each do |cmd|
53
+ return cmd if inspec.command(cmd).exist?
54
+ end
55
+
56
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `ipfstat`"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class IpNat < Inspec.resource(1)
4
+ name "ipnat"
5
+ supports platform: "bsd"
6
+ supports platform: "solaris"
7
+ desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT"
8
+ example <<~EXAMPLE
9
+ describe ipnat do
10
+ it { should have_rule("map net1 192.168.0.0/24 -> 0/32") }
11
+ end
12
+ EXAMPLE
13
+
14
+ def initialize
15
+ # checks if the instance is either bsd or solaris
16
+ return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
17
+
18
+ # ensures, all calls are aborted for non-supported os
19
+ @ipnat_cache = []
20
+ skip_resource "The `ipnat` resource is not supported on your OS yet."
21
+ end
22
+
23
+ def has_rule?(rule = nil)
24
+ # checks if the rule is part of the ruleset
25
+ retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
26
+ end
27
+
28
+ def retrieve_rules
29
+ # this would be true if the OS family was not bsd/solaris when checked in initliaze
30
+ return @ipnat_cache if defined?(@ipnat_cache)
31
+
32
+ # construct ipnat command to show the list of current IP NAT table entry mappings
33
+ bin = find_ipnat_or_error
34
+ ipnat_cmd = "#{bin} -l"
35
+ cmd = inspec.command(ipnat_cmd)
36
+
37
+ # Return empty array when command is not executed successfully
38
+ return [] if cmd.exit_status.to_i != 0
39
+
40
+ # split rules, returns array or rules
41
+ @ipnat_cache = cmd.stdout.split("\n").map(&:strip)
42
+ end
43
+
44
+ def to_s
45
+ "Ipnat"
46
+ end
47
+
48
+ private
49
+
50
+ def find_ipnat_or_error
51
+ %w{/usr/sbin/ipnat /sbin/ipnat ipnat}.each do |cmd|
52
+ return cmd if inspec.command(cmd).exist?
53
+ end
54
+
55
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `ipnat`"
56
+ end
57
+ end
58
+ end
@@ -4,10 +4,9 @@ module Inspec::Resources
4
4
  class Lines
5
5
  attr_reader :params
6
6
 
7
- def initialize(raw, desc, exit_status = nil)
7
+ def initialize(raw, desc)
8
8
  @params = raw
9
9
  @desc = desc
10
- @exit_status = exit_status
11
10
  end
12
11
 
13
12
  def to_s
@@ -80,11 +79,6 @@ module Inspec::Resources
80
79
  options[:ssl_cert] = @ssl_cert unless @ssl_cert.nil?
81
80
  options[:ssl_ca_cert] = @ssl_ca_cert unless @ssl_ca_cert.nil?
82
81
 
83
- # Setting the logger level to INFO as mongo gem version 2.13.2 is using DEBUG as the log level Ref: https://github.com/mongodb/mongo-ruby-driver/blob/v2.13.2/lib/mongo/logger.rb#L79
84
- # Latest version of the mongo gem don't have this issue as it set to INFO level Ref: https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/logger.rb#L82
85
- # We pinned the version to 2.13.2 as the latest version of the mongo gem has broken symlink https://jira.mongodb.org/browse/RUBY-2546 which causes omnibus build failure.
86
- # Once we get the latest version working we can remove logger level set here.
87
- Mongo::Logger.logger.level = Logger::INFO
88
82
  @client = Mongo::Client.new([ "#{host}:#{port}" ], options)
89
83
 
90
84
  rescue => e
@@ -61,13 +61,9 @@ module Inspec::Resources
61
61
  raise Inspec::Exceptions::ResourceFailed, "Oracle query with errors: #{out}"
62
62
  else
63
63
  begin
64
- unless inspec_cmd.stdout.empty?
65
- DatabaseHelper::SQLQueryResult.new(inspec_cmd, parse_csv_result(inspec_cmd.stdout))
66
- else
67
- inspec_cmd.stdout
68
- end
69
- rescue Exception => ex
70
- raise Inspec::Exceptions::ResourceFailed, "Oracle query with exception: #{ex}"
64
+ DatabaseHelper::SQLQueryResult.new(inspec_cmd, parse_csv_result(inspec_cmd.stdout))
65
+ rescue
66
+ raise Inspec::Exceptions::ResourceFailed, "Oracle query with errors: #{out}"
71
67
  end
72
68
  end
73
69
  end
@@ -91,31 +87,22 @@ module Inspec::Resources
91
87
  verified_query = verify_query(escaped_query)
92
88
  end
93
89
 
94
- sql_prefix, sql_postfix, oracle_echo_str = "", "", ""
90
+ sql_prefix, sql_postfix = "", ""
95
91
  if inspec.os.windows?
96
92
  sql_prefix = %{@'\n#{format_options}\n#{verified_query}\nEXIT\n'@ | }
97
93
  else
98
94
  sql_postfix = %{ <<'EOC'\n#{format_options}\n#{verified_query}\nEXIT\nEOC}
99
- # oracle_query_string is echoed to be able to extract the query output clearly
100
- oracle_echo_str = %{echo 'oracle_query_string';}
101
- end
102
-
103
- # Resetting sql_postfix if system is using AIX OS and C shell installation for oracle
104
- if inspec.os.aix?
105
- command_to_fetch_shell = @su_user ? %{su - #{@su_user} -c "env | grep SHELL"} : %{env | grep SHELL}
106
- shell_is_csh = inspec.command(command_to_fetch_shell).stdout&.include? "/csh"
107
- sql_postfix = %{ <<'EOC'\n#{format_options}\n#{verified_query}\nEXIT\n'EOC'} if shell_is_csh
108
95
  end
109
96
 
110
97
  if @db_role.nil?
111
- %{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service}#{sql_postfix}}
98
+ %{#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service}#{sql_postfix}}
112
99
  elsif @su_user.nil?
113
- %{#{oracle_echo_str}#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
100
+ %{#{sql_prefix}#{bin} #{user}/#{password}@#{host}:#{port}/#{@service} as #{@db_role}#{sql_postfix}}
114
101
  else
115
102
  # oracle_query_string is echoed to be able to extract the query output clearly
116
103
  # su - su_user in certain versions of oracle returns a message
117
104
  # Example of msg with query output: The Oracle base remains unchanged with value /oracle\n\nVALUE\n3\n
118
- %{su - #{@su_user} -c "#{oracle_echo_str} env ORACLE_SID=#{@service} #{@bin} / as #{@db_role}#{sql_postfix}"}
105
+ %{su - #{@su_user} -c "echo 'oracle_query_string'; env ORACLE_SID=#{@service} #{@bin} / as #{@db_role}#{sql_postfix}"}
119
106
  end
120
107
  end
121
108
 
@@ -4,9 +4,9 @@ require "shellwords" unless defined?(Shellwords)
4
4
 
5
5
  module Inspec::Resources
6
6
  class Lines
7
- attr_reader :output, :exit_status
7
+ attr_reader :output
8
8
 
9
- def initialize(raw, desc, exit_status)
9
+ def initialize(raw, desc)
10
10
  @output = raw
11
11
  @desc = desc
12
12
  end
@@ -55,12 +55,10 @@ module Inspec::Resources
55
55
  psql_cmd = create_psql_cmd(query, db)
56
56
  cmd = inspec.command(psql_cmd, redact_regex: %r{(:\/\/[a-z]*:).*(@)})
57
57
  out = cmd.stdout + "\n" + cmd.stderr
58
- if cmd.exit_status != 0 && ( out =~ /could not connect to/ || out =~ /password authentication failed/ ) && out.downcase =~ /error:/
59
- raise Inspec::Exceptions::ResourceFailed, "PostgreSQL connection error: #{out}"
60
- elsif cmd.exit_status != 0 && out.downcase =~ /error:/
61
- Lines.new(out, "PostgreSQL query with error: #{query}", cmd.exit_status)
58
+ if cmd.exit_status != 0 || out =~ /could not connect to .*/ || out.downcase =~ /^error:.*/
59
+ raise Inspec::Exceptions::ResourceFailed, "PostgreSQL query with errors: #{out}"
62
60
  else
63
- Lines.new(cmd.stdout.strip, "PostgreSQL query: #{query}", cmd.exit_status)
61
+ Lines.new(cmd.stdout.strip, "PostgreSQL query: #{query}")
64
62
  end
65
63
  end
66
64
 
@@ -43,7 +43,7 @@ module Inspec::Resources
43
43
 
44
44
  all_cmds = ps_axo
45
45
  @list = all_cmds.find_all do |hm|
46
- hm[:command] =~ grep || hm[:process_name] =~ grep
46
+ hm[:command] =~ grep
47
47
  end
48
48
  end
49
49
 
@@ -73,7 +73,6 @@ module Inspec::Resources
73
73
  .register_column(:time, field: "time")
74
74
  .register_column(:users, field: "user")
75
75
  .register_column(:commands, field: "command")
76
- .register_column(:process_name, field: "process_name")
77
76
  .install_filter_methods_on_resource(self, :filtered_processes)
78
77
 
79
78
  private
@@ -88,9 +87,9 @@ module Inspec::Resources
88
87
  if os.linux?
89
88
  command, regex, field_map = ps_configuration_for_linux
90
89
  elsif os.windows?
91
- command = '$Proc = Get-Process -IncludeUserName | Select-Object PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path,ProcessName | ConvertTo-Csv -NoTypeInformation;$Proc.Replace("""","").Replace("`r`n","`n")'
90
+ 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")'
92
91
  # Wanted to use /(?:^|,)([^,]*)/; works on rubular.com not sure why here?
93
- regex = /^(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*)$/
92
+ regex = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
94
93
  field_map = {
95
94
  pid: 2,
96
95
  cpu: 3,
@@ -103,7 +102,6 @@ module Inspec::Resources
103
102
  time: 10,
104
103
  user: 11,
105
104
  command: 12,
106
- process_name: 13,
107
105
  }
108
106
  else
109
107
  command = "ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command"
@@ -195,7 +193,7 @@ module Inspec::Resources
195
193
 
196
194
  # build a hash of process data that we'll turn into a struct for FilterTable
197
195
  process_data = {}
198
- %i{label pid cpu mem vsz rss tty stat start time user command process_name}.each do |param|
196
+ %i{label pid cpu mem vsz rss tty stat start time user command}.each do |param|
199
197
  # not all operating systems support all fields, so skip the field if we don't have it
200
198
  process_data[param] = line[field_map[param]] if field_map.key?(param)
201
199
  end
@@ -182,9 +182,7 @@ module Inspec::Resources
182
182
  when "aix"
183
183
  SrcMstr.new(inspec)
184
184
  when "amazon"
185
- # If `initctl` exists on the system, use `Upstart`. Else use `Systemd` since all-new Amazon Linux supports `systemctl`.
186
- # This way, it is not dependent on the version of Amazon Linux.
187
- if inspec.command("initctl").exist? || inspec.command("/sbin/initctl").exist?
185
+ if os[:release] =~ /^20\d\d/
188
186
  Upstart.new(inspec, service_ctl)
189
187
  else
190
188
  Systemd.new(inspec, service_ctl)
@@ -605,7 +603,7 @@ module Inspec::Resources
605
603
  return nil if srv.nil? || srv[0].nil?
606
604
 
607
605
  # extract values from service
608
- parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[\-0-9]+)\t(?<name>\S*)$/.match(srv[0])
606
+ parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[0-9]+)\t(?<name>\S*)$/.match(srv[0])
609
607
  enabled = !parsed_srv["name"].nil? # it's in the list
610
608
 
611
609
  # check if the service is running
@@ -8,21 +8,6 @@
8
8
  # glob so this remains a sort of manifest for our resources.
9
9
 
10
10
  require "inspec/resource"
11
-
12
- # Detect if we are running the stripped-down inspec-core
13
- # This relies on AWS being stripped from the inspec-core gem
14
- inspec_core_only = ENV["NO_AWS"] || !File.exist?(File.join(File.dirname(__FILE__), "..", "resource_support", "aws.rb"))
15
-
16
- # Do not attempt to load cloud resources if we are in inspec-core mode
17
- unless inspec_core_only
18
- require "resource_support/aws"
19
- require "resources/azure/azure_backend"
20
- require "resources/azure/azure_generic_resource"
21
- require "resources/azure/azure_resource_group"
22
- require "resources/azure/azure_virtual_machine"
23
- require "resources/azure/azure_virtual_machine_data_disk"
24
- end
25
-
26
11
  require "inspec/resources/aide_conf"
27
12
  require "inspec/resources/apache"
28
13
  require "inspec/resources/apache_conf"
@@ -41,6 +26,7 @@ require "inspec/resources/cassandradb_session"
41
26
  require "inspec/resources/cassandradb_conf"
42
27
  require "inspec/resources/cassandra"
43
28
  require "inspec/resources/crontab"
29
+ require "inspec/resources/cron"
44
30
  require "inspec/resources/timezone"
45
31
  require "inspec/resources/dh_params"
46
32
  require "inspec/resources/directory"
@@ -138,7 +124,8 @@ require "inspec/resources/xinetd_conf"
138
124
  require "inspec/resources/yum"
139
125
  require "inspec/resources/zfs_dataset"
140
126
  require "inspec/resources/zfs_pool"
141
-
127
+ require "inspec/resources/ipnat"
128
+ require "inspec/resources/ipfilter"
142
129
  # file formats, depend on json implementation
143
130
  require "inspec/resources/json"
144
131
  require "inspec/resources/yaml"
data/lib/inspec/rule.rb CHANGED
@@ -358,7 +358,7 @@ module Inspec
358
358
  # YAML will automagically give us a Date or a Time.
359
359
  # If transcoding YAML between languages (e.g. Go) the date might have also ended up as a String.
360
360
  # A string that does not represent a valid time results in the date 0000-01-01.
361
- if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.parse(expiry).year != 0)
361
+ if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.new(expiry).year != 0)
362
362
  expiry = expiry.to_time if expiry.is_a? Date
363
363
  expiry = Time.parse(expiry) if expiry.is_a? String
364
364
  if expiry < Time.now # If the waiver expired, return - no skip applied
data/lib/inspec/runner.rb CHANGED
@@ -105,6 +105,7 @@ module Inspec
105
105
 
106
106
  write_lockfile(profile) if @create_lockfile
107
107
  profile.locked_dependencies
108
+ profile.load_gem_dependencies
108
109
  profile_context = profile.load_libraries
109
110
 
110
111
  profile_context.dependencies.list.values.each do |requirement|
@@ -126,9 +127,25 @@ module Inspec
126
127
  end
127
128
  end
128
129
 
130
+ controls_count = 0
131
+ control_checks_count_map = {}
132
+
129
133
  all_controls.each do |rule|
130
- register_rule(rule) unless rule.nil?
134
+ unless rule.nil?
135
+ register_rule(rule)
136
+ checks = ::Inspec::Rule.prepare_checks(rule)
137
+ unless checks.empty?
138
+ # controls with empty tests are avoided
139
+ # checks represent tests within control
140
+ controls_count += 1
141
+ control_checks_count_map[rule.to_s] = checks.count
142
+ end
143
+ end
131
144
  end
145
+
146
+ # this sets data via runner-rspec into base RSpec formatter object, which gets used up within streaming plugins
147
+ @test_collector.set_controls_count(controls_count)
148
+ @test_collector.set_control_checks_count_map(control_checks_count_map)
132
149
  end
133
150
 
134
151
  def run(with = nil)
@@ -42,6 +42,21 @@ module Inspec
42
42
  end
43
43
  end
44
44
 
45
+ # These control count related methods are called from load logic of runner library of inspec
46
+ ######### Start of control count related methods
47
+ def set_controls_count(controls_count)
48
+ formatters.each do |fmt|
49
+ fmt.set_controls_count(controls_count)
50
+ end
51
+ end
52
+
53
+ def set_control_checks_count_map(mapping)
54
+ formatters.each do |fmt|
55
+ fmt.set_control_checks_count_map(mapping)
56
+ end
57
+ end
58
+ ######### end of control count related methods
59
+
45
60
  def backend
46
61
  formatters.first.backend
47
62
  end
@@ -11,16 +11,16 @@ module Inspec
11
11
  "additionalProperties" => true,
12
12
  "required" => %w{label data},
13
13
  "properties" => {
14
- "label" => Primitives::STRING,
15
- "data" => Primitives::STRING,
14
+ "label" => Primitives.desc(Primitives::STRING, "The type of description. Examples: 'fix' or 'check'."),
15
+ "data" => Primitives.desc(Primitives::STRING, "The text of the description."),
16
16
  },
17
- }, [])
17
+ }, [], "A description for a control.")
18
18
 
19
19
  # Lists the potential values for a control result
20
20
  CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", {
21
21
  "type" => "string",
22
22
  "enum" => %w{passed failed skipped error},
23
- }, [])
23
+ }, [], "The status of a control. Should be one of 'passed', 'failed', 'skipped', or 'error'.")
24
24
 
25
25
  # Represents the statistics/result of a control"s execution
26
26
  CONTROL_RESULT = Primitives::SchemaType.new("Control Result", {
@@ -28,24 +28,26 @@ module Inspec
28
28
  "additionalProperties" => true,
29
29
  "required" => %w{code_desc run_time start_time},
30
30
  "properties" => {
31
- "status" => CONTROL_RESULT_STATUS.ref,
32
- "code_desc" => Primitives::STRING,
33
- "run_time" => Primitives::NUMBER,
34
- "start_time" => Primitives::STRING,
31
+ "status" => Primitives.desc(CONTROL_RESULT_STATUS.ref, "The status of this test within the control. Example: 'failed'."),
32
+ "code_desc" => Primitives.desc(Primitives::STRING, "A description of this test. Example: 'limits.conf * is expected to include ['hard', 'maxlogins', '10']."),
33
+ "run_time" => Primitives.desc(Primitives::NUMBER, "The execution time in seconds for the test."),
34
+ "start_time" => Primitives.desc(Primitives::STRING, "The time at which the test started."),
35
35
 
36
36
  # All optional
37
- "resource" => Primitives::STRING,
38
- "message" => Primitives::STRING,
39
- "skip_message" => Primitives::STRING,
40
- "exception" => Primitives::STRING,
37
+ "resource" => Primitives.desc(Primitives::STRING, "The resource used in the test. Example: in Inspec, you can use the 'File' resource."),
38
+ "message" => Primitives.desc(Primitives::STRING, "An explanation of the test status - usually only provided when the test fails."),
39
+ "skip_message" => Primitives.desc(Primitives::STRING, "An explanation of the test status if the status was 'skipped."),
40
+ "exception" => Primitives.desc(Primitives::STRING, "The type of exception if an exception was thrown."),
41
+ "resource_id" => Primitives.desc(Primitives::STRING, "The unique identifier of the resource."),
41
42
  "backtrace" => {
42
43
  "anyOf" => [
43
44
  Primitives.array(Primitives::STRING),
44
45
  Primitives::NULL,
45
46
  ],
47
+ "description" => "The stacktrace/backtrace of the exception if one occurred.",
46
48
  },
47
49
  },
48
- }, [CONTROL_RESULT_STATUS])
50
+ }, [CONTROL_RESULT_STATUS], "A test within a control and its results and findings such as how long it took to run.")
49
51
 
50
52
  # Represents a control produced
51
53
  CONTROL = Primitives::SchemaType.new("Exec JSON Control", {
@@ -53,26 +55,25 @@ module Inspec
53
55
  "additionalProperties" => true,
54
56
  "required" => %w{id title desc impact refs tags code source_location results},
55
57
  "properties" => {
56
- "id" => Primitives.desc(Primitives::STRING, "The ID of this control"),
57
- "title" => { "type" => %w{string null} }, # Nullable string
58
- "desc" => { "type" => %w{string null} },
59
- "descriptions" => Primitives.array(CONTROL_DESCRIPTION.ref),
60
- "impact" => Primitives::IMPACT,
61
- "refs" => Primitives.array(Primitives::REFERENCE.ref),
62
- "tags" => Primitives::TAGS,
63
- "code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code"),
64
- "source_location" => Primitives::SOURCE_LOCATION.ref,
65
- "results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q{
66
- A list of all results of the controls describe blocks.
67
-
68
- For instance, if in the controls code we had the following:
69
- describe sshd_config do
70
- its('Port') { should cmp 22 }
71
- end
72
- The result of this block as a ControlResult would be appended to the results list.
73
- }),
58
+ "id" => Primitives.desc(Primitives::STRING, "The id."),
59
+ "title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), # Nullable string
60
+ "desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
61
+ "descriptions" => Primitives.desc(Primitives.array(CONTROL_DESCRIPTION.ref), "A set of additional descriptions. Example: the 'fix' text."),
62
+ "impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
63
+ "refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
64
+ "tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
65
+ "code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
66
+ "source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
67
+ "results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q(
68
+ The set of all tests within the control and their results and findings. Example:
69
+ For Chef Inspec, if in the control's code we had the following:
70
+ describe sshd_config do
71
+ its('Port') { should cmp 22 }
72
+ end
73
+ The findings from this block would be appended to the results, as well as those of any other blocks within the control.
74
+ )),
74
75
  },
75
- }, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT])
76
+ }, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
76
77
 
77
78
  # Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
78
79
  # However, concessions were made to the reality of current reporters, specifically
@@ -86,30 +87,30 @@ module Inspec
86
87
  # sha256, status, status_message
87
88
  "properties" => {
88
89
  # These are provided in inspec.yml
89
- "name" => Primitives::STRING,
90
- "title" => Primitives::STRING,
91
- "maintainer" => Primitives::STRING,
92
- "copyright" => Primitives::STRING,
93
- "copyright_email" => Primitives::STRING,
94
- "depends" => Primitives.array(Primitives::DEPENDENCY.ref),
95
- "parent_profile" => Primitives::STRING,
96
- "license" => Primitives::STRING,
97
- "summary" => Primitives::STRING,
98
- "version" => Primitives::STRING,
99
- "supports" => Primitives.array(Primitives::SUPPORT.ref),
100
- "description" => Primitives::STRING,
101
- "inspec_version" => Primitives::STRING,
90
+ "name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
91
+ "title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
92
+ "maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
93
+ "copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
94
+ "copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contact information of the copyright holder(s)."),
95
+ "depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."),
96
+ "parent_profile" => Primitives.desc(Primitives::STRING, "The name of the parent profile if the profile is a dependency of another."),
97
+ "license" => Primitives.desc(Primitives::STRING, "The copyright license. Example: the full text or the name, such as 'Apache License, Version 2.0'."),
98
+ "summary" => Primitives.desc(Primitives::STRING, "The summary. Example: the Security Technical Implementation Guide (STIG) header."),
99
+ "version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
100
+ "supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
101
+ "description" => Primitives.desc(Primitives::STRING, "The description - should be more detailed than the summary."),
102
+ "inspec_version" => Primitives.desc(Primitives::STRING, "The version of Inspec."),
102
103
 
103
104
  # These are generated at runtime, and all except status_message and skip_message are guaranteed
104
- "sha256" => Primitives::STRING,
105
- "status" => Primitives::STRING,
106
- "status_message" => Primitives::STRING, # If skipped or failed to load, why
107
- "skip_message" => Primitives::STRING, # Deprecated field storing reason for skipping. status_message should be used instead.
108
- "controls" => Primitives.array(CONTROL.ref),
109
- "groups" => Primitives.array(Primitives::CONTROL_GROUP.ref),
110
- "attributes" => Primitives.array(Primitives::INPUT),
105
+ "sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
106
+ "status" => Primitives.desc(Primitives::STRING, "The status. Example: loaded."), # enum? loaded, failed, skipped
107
+ "status_message" => Primitives.desc(Primitives::STRING, "The reason for the status. Example: why it was skipped or failed to load."),
108
+ "skip_message" => Primitives.desc(Primitives::STRING, "The reason for skipping if it was skipped."), # Deprecated field - status_message should be used instead.
109
+ "controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings."),
110
+ "groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
111
+ "attributes" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used in the run."),
111
112
  },
112
- }, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT])
113
+ }, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
113
114
 
114
115
  # Result of exec json. Top level value
115
116
  # TODO: Include the format of top level controls. This was omitted for lack of sufficient examples
@@ -118,12 +119,12 @@ module Inspec
118
119
  "additionalProperties" => true,
119
120
  "required" => %w{platform profiles statistics version},
120
121
  "properties" => {
121
- "platform" => Primitives::PLATFORM.ref,
122
- "profiles" => Primitives.array(PROFILE.ref),
123
- "statistics" => Primitives::STATISTICS.ref,
124
- "version" => Primitives::STRING,
122
+ "platform" => Primitives.desc(Primitives::PLATFORM.ref, "Information on the platform the run from the tool that generated the findings was from. Example: the name of the operating system."),
123
+ "profiles" => Primitives.desc(Primitives.array(PROFILE.ref), "Information on the run(s) from the tool that generated the findings. Example: the findings."),
124
+ "statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."),
125
+ "version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
125
126
  },
126
- }, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS])
127
+ }, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
127
128
  end
128
129
  end
129
130
  end