inspec-core 4.56.58 → 5.7.9

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