inspec-core 5.17.4 → 5.21.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +20 -17
  3. data/etc/deprecations.json +4 -0
  4. data/inspec-core.gemspec +23 -23
  5. data/lib/inspec/base_cli.rb +7 -0
  6. data/lib/inspec/cli.rb +68 -11
  7. data/lib/inspec/dependencies/dependency_set.rb +6 -2
  8. data/lib/inspec/dsl.rb +24 -5
  9. data/lib/inspec/enhanced_outcomes.rb +19 -0
  10. data/lib/inspec/env_printer.rb +1 -1
  11. data/lib/inspec/errors.rb +2 -0
  12. data/lib/inspec/exceptions.rb +4 -0
  13. data/lib/inspec/fetcher/url.rb +1 -1
  14. data/lib/inspec/file_provider.rb +36 -0
  15. data/lib/inspec/formatters/base.rb +69 -16
  16. data/lib/inspec/iaf_file.rb +127 -0
  17. data/lib/inspec/plugin/v2/loader.rb +19 -8
  18. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
  19. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
  20. data/lib/inspec/profile.rb +17 -7
  21. data/lib/inspec/reporters/base.rb +1 -0
  22. data/lib/inspec/reporters/cli.rb +94 -3
  23. data/lib/inspec/reporters/json.rb +3 -1
  24. data/lib/inspec/reporters/yaml.rb +3 -1
  25. data/lib/inspec/reporters.rb +2 -1
  26. data/lib/inspec/resources/aide_conf.rb +4 -0
  27. data/lib/inspec/resources/apache.rb +4 -0
  28. data/lib/inspec/resources/apache_conf.rb +4 -0
  29. data/lib/inspec/resources/apt.rb +6 -1
  30. data/lib/inspec/resources/audit_policy.rb +5 -0
  31. data/lib/inspec/resources/auditd_conf.rb +4 -0
  32. data/lib/inspec/resources/bash.rb +4 -0
  33. data/lib/inspec/resources/bond.rb +4 -0
  34. data/lib/inspec/resources/bridge.rb +4 -0
  35. data/lib/inspec/resources/cassandradb_conf.rb +5 -0
  36. data/lib/inspec/resources/cassandradb_session.rb +8 -3
  37. data/lib/inspec/resources/chocolatey_package.rb +4 -0
  38. data/lib/inspec/resources/chrony_conf.rb +4 -0
  39. data/lib/inspec/resources/command.rb +5 -0
  40. data/lib/inspec/resources/cpan.rb +4 -0
  41. data/lib/inspec/resources/cran.rb +4 -0
  42. data/lib/inspec/resources/cron.rb +5 -0
  43. data/lib/inspec/resources/csv.rb +6 -1
  44. data/lib/inspec/resources/dh_params.rb +4 -0
  45. data/lib/inspec/resources/docker_container.rb +4 -0
  46. data/lib/inspec/resources/docker_image.rb +4 -0
  47. data/lib/inspec/resources/docker_plugin.rb +4 -0
  48. data/lib/inspec/resources/docker_service.rb +4 -0
  49. data/lib/inspec/resources/etc_group.rb +4 -0
  50. data/lib/inspec/resources/etc_hosts_allow_deny.rb +5 -0
  51. data/lib/inspec/resources/file.rb +7 -2
  52. data/lib/inspec/resources/filesystem.rb +4 -0
  53. data/lib/inspec/resources/gem.rb +4 -0
  54. data/lib/inspec/resources/groups.rb +4 -0
  55. data/lib/inspec/resources/grub_conf.rb +4 -0
  56. data/lib/inspec/resources/host.rb +4 -0
  57. data/lib/inspec/resources/http.rb +6 -2
  58. data/lib/inspec/resources/ibmdb2_conf.rb +8 -0
  59. data/lib/inspec/resources/ibmdb2_session.rb +12 -3
  60. data/lib/inspec/resources/iis_app.rb +4 -0
  61. data/lib/inspec/resources/iis_app_pool.rb +4 -0
  62. data/lib/inspec/resources/iis_site.rb +4 -0
  63. data/lib/inspec/resources/inetd_conf.rb +4 -0
  64. data/lib/inspec/resources/interface.rb +4 -0
  65. data/lib/inspec/resources/ip6tables.rb +4 -0
  66. data/lib/inspec/resources/ipfilter.rb +4 -0
  67. data/lib/inspec/resources/ipnat.rb +4 -0
  68. data/lib/inspec/resources/iptables.rb +4 -0
  69. data/lib/inspec/resources/json.rb +4 -0
  70. data/lib/inspec/resources/kernel_module.rb +4 -0
  71. data/lib/inspec/resources/kernel_parameter.rb +4 -0
  72. data/lib/inspec/resources/key_rsa.rb +4 -0
  73. data/lib/inspec/resources/ksh.rb +4 -0
  74. data/lib/inspec/resources/limits_conf.rb +4 -0
  75. data/lib/inspec/resources/login_defs.rb +4 -0
  76. data/lib/inspec/resources/lxc.rb +65 -9
  77. data/lib/inspec/resources/mongodb.rb +4 -0
  78. data/lib/inspec/resources/mongodb_conf.rb +5 -0
  79. data/lib/inspec/resources/mongodb_session.rb +6 -1
  80. data/lib/inspec/resources/mount.rb +4 -0
  81. data/lib/inspec/resources/mssql_session.rb +4 -0
  82. data/lib/inspec/resources/mssql_sys_conf.rb +7 -0
  83. data/lib/inspec/resources/mysql_conf.rb +4 -0
  84. data/lib/inspec/resources/mysql_session.rb +8 -1
  85. data/lib/inspec/resources/nginx.rb +6 -1
  86. data/lib/inspec/resources/nginx_conf.rb +4 -0
  87. data/lib/inspec/resources/noop.rb +4 -0
  88. data/lib/inspec/resources/npm.rb +4 -0
  89. data/lib/inspec/resources/ntp_conf.rb +4 -0
  90. data/lib/inspec/resources/oneget.rb +4 -0
  91. data/lib/inspec/resources/opa_api.rb +10 -0
  92. data/lib/inspec/resources/opa_cli.rb +14 -0
  93. data/lib/inspec/resources/oracledb_conf.rb +5 -0
  94. data/lib/inspec/resources/oracledb_listener_conf.rb +4 -0
  95. data/lib/inspec/resources/oracledb_session.rb +23 -4
  96. data/lib/inspec/resources/os.rb +4 -0
  97. data/lib/inspec/resources/os_env.rb +4 -0
  98. data/lib/inspec/resources/package.rb +4 -0
  99. data/lib/inspec/resources/parse_config.rb +10 -1
  100. data/lib/inspec/resources/pip.rb +4 -0
  101. data/lib/inspec/resources/platform.rb +4 -0
  102. data/lib/inspec/resources/podman.rb +353 -0
  103. data/lib/inspec/resources/podman_container.rb +84 -0
  104. data/lib/inspec/resources/podman_image.rb +108 -0
  105. data/lib/inspec/resources/podman_network.rb +81 -0
  106. data/lib/inspec/resources/podman_pod.rb +101 -0
  107. data/lib/inspec/resources/podman_volume.rb +87 -0
  108. data/lib/inspec/resources/postfix_conf.rb +4 -0
  109. data/lib/inspec/resources/postgres_conf.rb +4 -0
  110. data/lib/inspec/resources/postgres_session.rb +8 -4
  111. data/lib/inspec/resources/powershell.rb +4 -0
  112. data/lib/inspec/resources/processes.rb +6 -4
  113. data/lib/inspec/resources/rabbitmq_config.rb +4 -0
  114. data/lib/inspec/resources/registry_key.rb +4 -0
  115. data/lib/inspec/resources/security_identifier.rb +4 -0
  116. data/lib/inspec/resources/security_policy.rb +4 -0
  117. data/lib/inspec/resources/service.rb +5 -1
  118. data/lib/inspec/resources/ssh_config.rb +4 -0
  119. data/lib/inspec/resources/sybase_conf.rb +4 -0
  120. data/lib/inspec/resources/sybase_session.rb +4 -0
  121. data/lib/inspec/resources/sys_info.rb +4 -0
  122. data/lib/inspec/resources/timezone.rb +4 -0
  123. data/lib/inspec/resources/users.rb +4 -0
  124. data/lib/inspec/resources/vbscript.rb +5 -0
  125. data/lib/inspec/resources/virtualization.rb +4 -0
  126. data/lib/inspec/resources/windows_feature.rb +5 -1
  127. data/lib/inspec/resources/windows_firewall.rb +4 -0
  128. data/lib/inspec/resources/windows_firewall_rule.rb +4 -0
  129. data/lib/inspec/resources/windows_hotfix.rb +4 -0
  130. data/lib/inspec/resources/windows_task.rb +4 -0
  131. data/lib/inspec/resources/wmi.rb +4 -0
  132. data/lib/inspec/resources/x509_certificate.rb +59 -0
  133. data/lib/inspec/resources/yum.rb +4 -0
  134. data/lib/inspec/resources/zfs_dataset.rb +4 -0
  135. data/lib/inspec/resources/zfs_pool.rb +4 -0
  136. data/lib/inspec/rule.rb +55 -18
  137. data/lib/inspec/run_data/control.rb +6 -0
  138. data/lib/inspec/run_data/statistics.rb +8 -2
  139. data/lib/inspec/runner.rb +18 -8
  140. data/lib/inspec/runner_rspec.rb +3 -2
  141. data/lib/inspec/schema/exec_json.rb +78 -2
  142. data/lib/inspec/schema/output_schema.rb +4 -1
  143. data/lib/inspec/schema/profile_json.rb +46 -0
  144. data/lib/inspec/schema.rb +91 -0
  145. data/lib/inspec/secrets/yaml.rb +7 -1
  146. data/lib/inspec/ui.rb +1 -0
  147. data/lib/inspec/utils/convert.rb +8 -0
  148. data/lib/inspec/utils/podman.rb +24 -0
  149. data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
  150. data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
  151. data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
  152. data/lib/inspec/utils/yaml_profile_summary.rb +34 -0
  153. data/lib/inspec/version.rb +1 -1
  154. data/lib/inspec/waiver_file_reader.rb +61 -0
  155. data/lib/matchers/matchers.rb +7 -1
  156. data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
  157. data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
  158. data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
  159. data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
  160. data/lib/plugins/inspec-reporter-html2/README.md +1 -1
  161. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +11 -5
  162. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +11 -7
  163. data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
  164. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -1
  165. data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
  166. data/lib/plugins/{inspec-artifact/inspec-artifact.gemspec → inspec-sign/inspec-sign.gemspec} +2 -2
  167. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +164 -0
  168. data/lib/plugins/{inspec-artifact/lib/inspec-artifact → inspec-sign/lib/inspec-sign}/cli.rb +14 -23
  169. data/lib/plugins/inspec-sign/lib/inspec-sign.rb +12 -0
  170. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
  171. data/lib/source_readers/inspec.rb +8 -2
  172. metadata +33 -15
  173. data/lib/plugins/inspec-artifact/lib/inspec-artifact/base.rb +0 -187
  174. data/lib/plugins/inspec-artifact/lib/inspec-artifact.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b31dbb074483f274162eeea0fde1b234cd19e1c65257e19f7d0bc2f46c375b70
4
- data.tar.gz: 31756fedad66edc248e5ae7b1ca5ab08408f6d7d6e6ce6dfb9053d1323c1acac
3
+ metadata.gz: 7f6f86730baadd988c363a823e5e46ba68c318e48b27bbaaba614ce7c33bb71c
4
+ data.tar.gz: f02ca42e9d4e525b82e9cfa61a04b122dc3b09c1e3c96f2ec4440c3fb3053f25
5
5
  SHA512:
6
- metadata.gz: e765163668a3799ae45fbe2a5ddd219832341c32b01b62422506324a62c44b99ad771f47c662be5abb1198fdd65969e2a038f54c5059841561edda42170f7631
7
- data.tar.gz: bbbadea0724f15d1a67c895eab750eea1a660bfc2e65143af703f8b5a88eb9e0c9fde1f9c08356fd9e4b32d202d620902fbad8784618cc01eec866c20f5247c7
6
+ metadata.gz: 597fed943b00ed280a749f05c97d01e29b4d5a85034835e9f242990588b35b740bf56a886b65202777d68b79b5cbd1faf1a48a69c1a4f1f8a00a83ecdece3527
7
+ data.tar.gz: 13aa012d46677b3cd31614dcc118c62b1aa1656f3007d6b105f7822a3c25a45b1b1284eef9fb9e761cf5a03cd8209ffe8518d54ed355c189a2f141788a000694
data/Gemfile CHANGED
@@ -23,13 +23,12 @@ group :omnibus do
23
23
  end
24
24
 
25
25
  group :test do
26
- gem "chefstyle", "~> 2.0.3"
26
+ gem "chefstyle", "~> 2.2.2"
27
27
  gem "concurrent-ruby", "~> 1.0"
28
- gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
29
28
  gem "json_schemer", ">= 0.2.1", "< 0.2.19"
30
29
  gem "m"
31
30
  gem "minitest-sprint", "~> 1.0"
32
- gem "minitest", "~> 5.5"
31
+ gem "minitest", "5.15.0"
33
32
  gem "mocha", "~> 1.1"
34
33
  gem "nokogiri", "~> 1.9"
35
34
  gem "pry-byebug"
@@ -39,26 +38,30 @@ group :test do
39
38
  gem "simplecov", "~> 0.21"
40
39
  gem "simplecov_json_formatter"
41
40
  gem "webmock", "~> 3.0"
41
+
42
+ if Gem.ruby_version >= Gem::Version.new("3.0.0")
43
+ # html-proofer has a dep on io-event, which is ruby-3 only
44
+ gem "html-proofer", "~> 3.19.4", platforms: :ruby # do not attempt to run proofer on windows. Pinned to 3.19.4 as test is breaking in updated versions.
45
+ end
42
46
  end
43
47
 
44
48
  group :deploy do
45
49
  gem "inquirer"
46
50
  end
47
51
 
48
- # Only include Test Kitchen support if we are on Ruby 2.7 or higher
49
- # as chef-zero support requires Ruby 2.6
50
- # See https://github.com/inspec/inspec/pull/5341
51
- if Gem.ruby_version >= Gem::Version.new("2.7.0")
52
- group :kitchen do
53
- gem "berkshelf"
54
- gem "chef", ">= 16.0" # Required to allow net-ssh > 6
55
- gem "test-kitchen", ">= 2.8"
56
- gem "kitchen-inspec", ">= 2.0"
57
- gem "kitchen-dokken", ">= 2.11"
58
- gem "git"
52
+ group :kitchen do
53
+ gem "berkshelf"
54
+
55
+ # Chef 18 requires ruby 3
56
+ if Gem.ruby_version >= Gem::Version.new("3.0.0")
57
+ gem "chef", ">= 17.0"
58
+ else
59
+ # Ruby 2.7 presumably - TODO remove this when 2.7 is sunsetted
60
+ gem "chef", "~> 16.0"
59
61
  end
60
- end
61
62
 
62
- if Gem.ruby_version < Gem::Version.new("2.7.0")
63
- gem "activesupport", "6.1.4.4"
63
+ gem "test-kitchen", ">= 2.8"
64
+ gem "kitchen-inspec", ">= 2.0"
65
+ gem "kitchen-dokken", ">= 2.11"
66
+ gem "git"
64
67
  end
@@ -121,6 +121,10 @@
121
121
  "cli_option_target_id":{
122
122
  "action": "warn",
123
123
  "prefix": "The --target-id option is deprecated in InSpec 5. Its value will be ignored."
124
+ },
125
+ "renamed_to_inspec_export":{
126
+ "action": "ignore",
127
+ "prefix": "The `inspec json` command is deprecated in InSpec 5 and replaced with `inspec export` command."
124
128
  }
125
129
  }
126
130
  }
data/inspec-core.gemspec CHANGED
@@ -18,32 +18,32 @@ Gem::Specification.new do |spec|
18
18
  # the gemfile and gemspec are necessary for appbundler so don't remove it
19
19
  spec.files =
20
20
  Dir.glob("{{lib,etc}/**/*,LICENSE,Gemfile,inspec-core.gemspec}")
21
- .grep_v(%r{(?<!inspec-init/templates/profiles/)(aws|azure|gcp)})
21
+ .grep_v(%r{(?<!inspec-init/templates/profiles/)(aws|azure|gcp|alicloud)})
22
22
  .grep_v(%r{lib/plugins/.*/test/})
23
23
  .reject { |f| File.directory?(f) }
24
24
 
25
25
  # Implementation dependencies
26
- spec.add_dependency "chef-telemetry", "~> 1.0", ">= 1.0.8" # 1.0.8+ removes the http dep
27
- spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
28
- spec.add_dependency "thor", ">= 0.20", "< 2.0"
29
- spec.add_dependency "method_source", ">= 0.8", "< 2.0"
30
- spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
31
- spec.add_dependency "rspec", ">= 3.9", "<= 3.11"
32
- spec.add_dependency "rspec-its", "~> 1.2"
33
- spec.add_dependency "pry", "~> 0.13"
34
- spec.add_dependency "hashie", ">= 3.4", "< 5.0"
35
- spec.add_dependency "mixlib-log", "~> 3.0"
36
- spec.add_dependency "sslshake", "~> 1.2"
37
- spec.add_dependency "parallel", "~> 1.9"
38
- spec.add_dependency "faraday", ">= 0.9.0", "< 1.5"
39
- spec.add_dependency "faraday_middleware", "~> 1.0"
40
- spec.add_dependency "tty-table", "~> 0.10"
41
- spec.add_dependency "tty-prompt", "~> 0.17"
42
- spec.add_dependency "tomlrb", ">= 1.2", "< 2.1"
43
- spec.add_dependency "addressable", "~> 2.4"
44
- spec.add_dependency "parslet", ">= 1.5", "< 2.0" # Pinned < 2.0, see #5389
45
- spec.add_dependency "semverse", "~> 3.0"
46
- spec.add_dependency "multipart-post", "~> 2.0"
26
+ spec.add_dependency "chef-telemetry", "~> 1.0", ">= 1.0.8" # 1.0.8+ removes the http dep
27
+ spec.add_dependency "license-acceptance", ">= 0.2.13", "< 3.0"
28
+ spec.add_dependency "thor", ">= 0.20", "< 2.0"
29
+ spec.add_dependency "method_source", ">= 0.8", "< 2.0"
30
+ spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
31
+ spec.add_dependency "rspec", ">= 3.9", "<= 3.11"
32
+ spec.add_dependency "rspec-its", "~> 1.2"
33
+ spec.add_dependency "pry", "~> 0.13"
34
+ spec.add_dependency "hashie", ">= 3.4", "< 5.0"
35
+ spec.add_dependency "mixlib-log", "~> 3.0"
36
+ spec.add_dependency "sslshake", "~> 1.2"
37
+ spec.add_dependency "parallel", "~> 1.9"
38
+ spec.add_dependency "faraday", ">= 1", "< 3"
39
+ spec.add_dependency "faraday-follow_redirects", "~> 0.3"
40
+ spec.add_dependency "tty-table", "~> 0.10"
41
+ spec.add_dependency "tty-prompt", "~> 0.17"
42
+ spec.add_dependency "tomlrb", ">= 1.2", "< 2.1"
43
+ spec.add_dependency "addressable", "~> 2.4"
44
+ spec.add_dependency "parslet", ">= 1.5", "< 2.0" # Pinned < 2.0, see #5389
45
+ spec.add_dependency "semverse", "~> 3.0"
46
+ spec.add_dependency "multipart-post", "~> 2.0"
47
47
 
48
- spec.add_dependency "train-core", "~> 3.0"
48
+ spec.add_dependency "train-core", "~> 3.10"
49
49
  end
@@ -138,6 +138,8 @@ module Inspec
138
138
  desc: "Provides path to Docker API endpoint (Docker)"
139
139
  option :ssh_config_file, type: :array,
140
140
  desc: "A list of paths to the ssh config file, e.g ~/.ssh/config or /etc/ssh/ssh_config"
141
+ option :podman_url, type: :string,
142
+ desc: "Provides path to Podman API endpoint"
141
143
  end
142
144
 
143
145
  def self.profile_options
@@ -203,6 +205,8 @@ module Inspec
203
205
  long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
204
206
  option :reporter_include_source, type: :boolean, default: false,
205
207
  desc: "Include full source code of controls in the CLI report"
208
+ option :enhanced_outcomes, type: :boolean,
209
+ desc: "Show enhanced outcomes in output"
206
210
  end
207
211
 
208
212
  def self.help(*args)
@@ -323,6 +327,9 @@ module Inspec
323
327
 
324
328
  def pretty_handle_exception(exception)
325
329
  case exception
330
+ when Inspec::InvalidProfileSignature
331
+ $stderr.puts exception.message
332
+ Inspec::UI.new.exit(:bad_signature)
326
333
  when Inspec::Error
327
334
  $stderr.puts exception.message
328
335
  exit(1)
data/lib/inspec/cli.rb CHANGED
@@ -5,6 +5,7 @@ require "inspec/dist"
5
5
  require "inspec/backend"
6
6
  require "inspec/dependencies/cache"
7
7
  require "inspec/utils/json_profile_summary"
8
+ require "inspec/utils/yaml_profile_summary"
8
9
 
9
10
  module Inspec # TODO: move this somewhere "better"?
10
11
  autoload :BaseCLI, "inspec/base_cli"
@@ -69,25 +70,77 @@ class Inspec::InspecCLI < Inspec::BaseCLI
69
70
  desc: "A list of tags to filter controls and include only those. Ignore all other tests."
70
71
  profile_options
71
72
  def json(target)
72
- require "json" unless defined?(JSON)
73
+ # This deprecation warning is ignored currently.
74
+ Inspec.deprecate(:renamed_to_inspec_export)
75
+ export(target, true)
76
+ end
73
77
 
78
+ desc "export PATH", "read the profile in PATH and generate a summary in the given format."
79
+ option :what, type: :string,
80
+ desc: "What to export: profile (default), readme, metadata."
81
+ option :format, type: :string,
82
+ desc: "The output format to use: json, raw, yaml. If valid format is not provided then it will use the default for the given 'what'."
83
+ option :output, aliases: :o, type: :string,
84
+ desc: "Save the created output to a path"
85
+ option :controls, type: :array,
86
+ desc: "For --what=profile, a list of controls to include. Ignore all other tests."
87
+ option :tags, type: :array,
88
+ desc: "For --what=profile, a list of tags to filter controls and include only those. Ignore all other tests."
89
+ profile_options
90
+ def export(target, as_json = false)
74
91
  o = config
75
92
  diagnose(o)
76
93
  o["log_location"] = $stderr
77
94
  configure_logger(o)
78
95
 
96
+ # using dup to resolve "can't modify frozen String" error.
97
+ what = o[:what].dup || "profile"
98
+ what.downcase!
99
+ raise Inspec::Error.new("Unrecognized option '#{what}' for --what - expected one of profile, readme, or metadata.") unless %w{profile readme metadata}.include?(what)
100
+
101
+ default_format_for_what = {
102
+ "profile" => "yaml",
103
+ "metadata" => "raw",
104
+ "readme" => "raw",
105
+ }
106
+ valid_formats_for_what = {
107
+ "profile" => %w{yaml json},
108
+ "metadata" => %w{yaml raw}, # not going to argue
109
+ "readme" => ["raw"],
110
+ }
111
+ format = o[:format] || default_format_for_what[what]
112
+ # default is json if we were called as old json command
113
+ format = "json" if as_json
114
+ raise Inspec::Error.new("Invalid option '#{format}' for --format and --what combination") unless format && valid_formats_for_what[what].include?(format)
115
+
79
116
  o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
80
117
  o[:check_mode] = true
81
118
  o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
82
-
83
119
  profile = Inspec::Profile.for_target(target, o)
84
120
  dst = o[:output].to_s
85
121
 
86
- # Write JSON
87
- Inspec::Utils::JsonProfileSummary.produce_json(
88
- info: profile.info,
89
- write_path: dst
90
- )
122
+ case what
123
+ when "profile"
124
+ if format == "json"
125
+ require "json" unless defined?(JSON)
126
+ # Write JSON
127
+ Inspec::Utils::JsonProfileSummary.produce_json(
128
+ info: profile.info,
129
+ write_path: dst
130
+ )
131
+ elsif format == "yaml"
132
+ Inspec::Utils::YamlProfileSummary.produce_yaml(
133
+ info: profile.info,
134
+ write_path: dst
135
+ )
136
+ end
137
+ when "readme"
138
+ out = dst.empty? ? $stdout : File.open(dst, "w")
139
+ out.write(profile.readme)
140
+ when "metadata"
141
+ out = dst.empty? ? $stdout : File.open(dst, "w")
142
+ out.write(profile.metadata_src)
143
+ end
91
144
  rescue StandardError => e
92
145
  pretty_handle_exception(e)
93
146
  end
@@ -189,12 +242,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
189
242
  desc: "Fallback to using local archives if fetching fails."
190
243
  option :ignore_errors, type: :boolean, default: false,
191
244
  desc: "Ignore profile warnings."
192
- def archive(path)
245
+ def archive(path, log_level = nil)
193
246
  o = config
194
247
  diagnose(o)
195
248
 
196
249
  o[:logger] = Logger.new($stdout)
197
- o[:logger].level = get_log_level(o[:log_level])
250
+ o[:logger].level = get_log_level(log_level || o[:log_level])
198
251
  o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
199
252
 
200
253
  # Force vendoring with overwrite when archiving
@@ -362,6 +415,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
362
415
  desc: "Load one or more input files, a YAML file with values for the shell to use"
363
416
  option :input, type: :array, banner: "name1=value1 name2=value2",
364
417
  desc: "Specify one or more inputs directly on the command line to the shell, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
418
+ option :enhanced_outcomes, type: :boolean,
419
+ desc: "Show enhanced outcomes in output"
365
420
  def shell_func
366
421
  o = config
367
422
  deprecate_target_id(config)
@@ -408,11 +463,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
408
463
  pretty_handle_exception(e)
409
464
  end
410
465
 
466
+ option :enhanced_outcomes, type: :boolean,
467
+ desc: "Show enhanced outcomes output"
411
468
  desc "schema NAME", "print the JSON schema", hide: true
412
469
  def schema(name)
413
470
  require "inspec/schema/output_schema"
414
-
415
- puts Inspec::Schema::OutputSchema.json(name)
471
+ o = config
472
+ puts Inspec::Schema::OutputSchema.json(name, o)
416
473
  rescue StandardError => e
417
474
  puts e
418
475
  puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
@@ -25,7 +25,9 @@ module Inspec
25
25
  def self.from_array(dependencies, cwd, cache, backend)
26
26
  dep_list = {}
27
27
  dependencies.each do |d|
28
- dep_list[d.name] = d
28
+ # if depedent profile does not have a source version then only name is used in dependency hash
29
+ key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue "#{d.name}"
30
+ dep_list[key_name] = d
29
31
  end
30
32
  new(cwd, cache, dep_list, backend)
31
33
  end
@@ -39,7 +41,9 @@ module Inspec
39
41
  def self.flatten_dep_tree(dep_tree)
40
42
  dep_list = {}
41
43
  dep_tree.each do |d|
42
- dep_list[d.name] = d
44
+ # if depedent profile does not have a source version then only name is used in dependency hash
45
+ key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue d.name
46
+ dep_list[key_name] = d
43
47
  dep_list.merge!(flatten_dep_tree(d.dependencies))
44
48
  end
45
49
  dep_list
data/lib/inspec/dsl.rb CHANGED
@@ -6,13 +6,13 @@ require "inspec/utils/deprecated_cloud_resources_list"
6
6
  module Inspec::DSL
7
7
  attr_accessor :backend
8
8
 
9
- def require_controls(id, &block)
10
- opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies }
9
+ def require_controls(id, version = nil, &block)
10
+ opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version }
11
11
  ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block)
12
12
  end
13
13
 
14
- def include_controls(id, &block)
15
- opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies }
14
+ def include_controls(id, version = nil, &block)
15
+ opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version }
16
16
  ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block)
17
17
  end
18
18
 
@@ -85,7 +85,26 @@ module Inspec::DSL
85
85
  def self.load_spec_files_for_profile(bind_context, opts, &block)
86
86
  dependencies = opts[:dependencies]
87
87
  profile_id = opts[:profile_id]
88
- dep_entry = dependencies.list[profile_id]
88
+ profile_version = opts[:profile_version]
89
+
90
+ new_profile_id = nil
91
+ if profile_version
92
+ new_profile_id = "#{profile_id}-#{profile_version}"
93
+ else
94
+ # This scary regex is used to match version following semantic Versioning (SemVer). Thanks to https://ihateregex.io/expr/semver/
95
+ regex_for_semver = /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/
96
+ dependencies.list.keys.each do |key|
97
+ # 1. Fetching VERSION from a profile dependency name which is in a format NAME-VERSION.
98
+ # 2. Matching original profile dependency name with profile name used with include or require control DSL.
99
+ fetching_semver = key.match(regex_for_semver).to_s
100
+ unless fetching_semver.nil? || fetching_semver.empty?
101
+ profile_id_key = key.split("-#{fetching_semver}")[0]
102
+ new_profile_id = key if profile_id_key == profile_id
103
+ end
104
+ end
105
+ end
106
+ # If dep profile does not contain a source version, key does not contain a version as well. In that case new_profile_id will be always nil and instead profile_id would be used to fetch profile from dependency list.
107
+ dep_entry = new_profile_id ? dependencies.list[new_profile_id] : dependencies.list[profile_id]
89
108
 
90
109
  if dep_entry.nil?
91
110
  raise <<~EOF
@@ -0,0 +1,19 @@
1
+ module Inspec
2
+ module EnhancedOutcomes
3
+
4
+ def self.determine_status(results, impact)
5
+ # No-op exception occurs in case of not_applicable_if
6
+ if results.any? { |r| !r[:exception].nil? && !r[:backtrace].nil? && r[:resource_class] != "noop" }
7
+ "error"
8
+ elsif !impact.nil? && impact.to_f == 0.0
9
+ "not_applicable"
10
+ elsif results.all? { |r| r[:status] == "skipped" }
11
+ "not_reviewed"
12
+ elsif results.any? { |r| r[:status] == "failed" }
13
+ "failed"
14
+ else
15
+ "passed"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -35,7 +35,7 @@ module Inspec
35
35
  private
36
36
 
37
37
  def print_completion_for_shell
38
- erb = ERB.new(File.read(completion_template_path), nil, "-")
38
+ erb = ERB.new(File.read(completion_template_path), trim_mode: "-")
39
39
  puts erb.result(TemplateContext.new(@command_class).get_bindings)
40
40
  end
41
41
 
data/lib/inspec/errors.rb CHANGED
@@ -22,4 +22,6 @@ module Inspec
22
22
  attr_accessor :gem_name
23
23
  attr_accessor :version
24
24
  end
25
+
26
+ class InvalidProfileSignature < Error; end
25
27
  end
@@ -8,5 +8,9 @@ module Inspec
8
8
  class ResourceFailed < StandardError; end
9
9
  class ResourceSkipped < StandardError; end
10
10
  class SecretsBackendNotFound < ArgumentError; end
11
+ class ProfileValidationKeyNotFound < ArgumentError; end
12
+ class ProfileSigningKeyNotFound < ArgumentError; end
13
+ class WaiversFileNotReadable < ArgumentError; end
14
+ class WaiversFileDoesNotExist < ArgumentError; end
11
15
  end
12
16
  end
@@ -262,7 +262,7 @@ module Inspec::Fetcher
262
262
 
263
263
  open(target, opts)
264
264
 
265
- rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e
265
+ rescue SocketError, Net::OpenTimeout, Errno::ECONNREFUSED, OpenURI::HTTPError => e
266
266
  raise Inspec::FetcherFailure, "Profile URL dependency #{target} could not be fetched: #{e.message}"
267
267
  end
268
268
 
@@ -2,6 +2,7 @@ require "rubygems/package" unless defined?(Gem::Package)
2
2
  require "pathname" unless defined?(Pathname)
3
3
  require "zlib" unless defined?(Zlib)
4
4
  require "zip" unless defined?(Zip)
5
+ require "inspec/iaf_file"
5
6
 
6
7
  module Inspec
7
8
  class FileProvider
@@ -14,6 +15,13 @@ module Inspec
14
15
  TarProvider.new(path)
15
16
  elsif File.exist?(path) && path.end_with?(".zip")
16
17
  ZipProvider.new(path)
18
+ elsif File.exist?(path) && path.end_with?(".iaf")
19
+ iaf_file = IafFile.new(path)
20
+ if iaf_file.valid?
21
+ IafProvider.new(path)
22
+ else
23
+ raise Inspec::InvalidProfileSignature, "Profile signature is invalid."
24
+ end
17
25
  elsif File.exist?(path)
18
26
  DirProvider.new(path)
19
27
  else
@@ -216,6 +224,34 @@ module Inspec
216
224
  end
217
225
  end # class TarProvider
218
226
 
227
+ class IafProvider < TarProvider
228
+ attr_reader :files
229
+
230
+ def initialize(path)
231
+ f = File.open(path, "rb")
232
+ version = f.readline.strip!
233
+ if version == "INSPEC-PROFILE-1"
234
+ while f.readline != "\n" do end
235
+ content = f.read
236
+ f.close
237
+ elsif version == "INSPEC-PROFILE-2"
238
+ f.readline.strip!
239
+ content = f.read
240
+ f.close
241
+ content = content.slice(490, content.length).lstrip
242
+ else
243
+ raise Inspec::InvalidProfileSignature, "Unrecognized IAF version."
244
+ end
245
+
246
+ tmpfile = nil
247
+ Dir.mktmpdir do |workdir|
248
+ tmpfile = Pathname.new(workdir).join("temp_profile.tar.gz")
249
+ File.open(tmpfile, "wb") { |fl| fl.write(content) }
250
+ super(tmpfile)
251
+ end
252
+ end
253
+ end # class IafProvider
254
+
219
255
  class RelativeFileProvider
220
256
  BLACKLIST_FILES = [
221
257
  "/pax_global_header",
@@ -1,12 +1,13 @@
1
1
  require "rspec/core"
2
2
  require "rspec/core/formatters/base_formatter"
3
3
  require "set" unless defined?(Set)
4
+ require "inspec/enhanced_outcomes"
4
5
 
5
6
  module Inspec::Formatters
6
7
  class Base < RSpec::Core::Formatters::BaseFormatter
7
8
  RSpec::Core::Formatters.register self, :close, :dump_summary, :stop
8
9
 
9
- attr_accessor :backend, :run_data
10
+ attr_accessor :backend, :run_data, :enhanced_outcomes
10
11
 
11
12
  def initialize(output)
12
13
  super(output)
@@ -17,6 +18,7 @@ module Inspec::Formatters
17
18
  @backend = nil
18
19
  @all_controls_count = nil
19
20
  @control_checks_count_map = {}
21
+ @enhanced_outcomes = nil
20
22
  end
21
23
 
22
24
  # RSpec Override: #dump_summary
@@ -50,7 +52,6 @@ module Inspec::Formatters
50
52
  else
51
53
  hash[:message] = exception_message(e)
52
54
  end
53
-
54
55
  next if e.is_a? RSpec::Expectations::ExpectationNotMetError
55
56
 
56
57
  hash[:exception] = e.class.name
@@ -68,6 +69,8 @@ module Inspec::Formatters
68
69
  # flesh out the profiles key with additional profile information
69
70
  run_data[:profiles] = profiles_info
70
71
 
72
+ add_enhanced_outcomes_to_controls if enhanced_outcomes
73
+
71
74
  # add the platform information for this particular target
72
75
  run_data[:platform] = {
73
76
  name: platform(:name),
@@ -110,6 +113,20 @@ module Inspec::Formatters
110
113
 
111
114
  private
112
115
 
116
+ def add_enhanced_outcomes_to_controls
117
+ all_unique_controls.each do |control|
118
+ control[:status] = determine_control_enhanced_outcome(control)
119
+ end
120
+ end
121
+
122
+ def determine_control_enhanced_outcome(control)
123
+ if control[:results]
124
+ Inspec::EnhancedOutcomes.determine_status(control[:results], control[:impact])
125
+ else
126
+ "passed"
127
+ end
128
+ end
129
+
113
130
  def all_unique_controls
114
131
  unique_controls = Set.new
115
132
  run_data[:profiles].each do |profile|
@@ -120,25 +137,59 @@ module Inspec::Formatters
120
137
  end
121
138
 
122
139
  def statistics
140
+ error = 0
141
+ not_applicable = 0
142
+ not_reviewed = 0
123
143
  failed = 0
124
- skipped = 0
125
144
  passed = 0
145
+ skipped = 0
146
+ enhanced_outcomes_summary = {}
147
+ if enhanced_outcomes
148
+ all_unique_controls.each do |control|
149
+
150
+ if control[:status] == "error"
151
+ error += 1
152
+ elsif control[:status] == "not_applicable"
153
+ not_applicable += 1
154
+ elsif control[:status] == "not_reviewed"
155
+ not_reviewed += 1
156
+ elsif control[:status] == "failed"
157
+ failed += 1
158
+ elsif control[:status] == "passed"
159
+ passed += 1
160
+ end
126
161
 
127
- all_unique_controls.each do |control|
128
- next unless control[:results]
129
-
130
- if control[:results].any? { |r| r[:status] == "failed" }
131
- failed += 1
132
- elsif control[:results].any? { |r| r[:status] == "skipped" }
133
- skipped += 1
134
- else
135
- passed += 1
136
- end
137
- end
162
+ # added this additionally because stats summary is also used for determining exit code in runner rspec
163
+ skipped += 1 if control[:results].any? { |r| r[:status] == "skipped" }
138
164
 
139
- total = failed + passed + skipped
165
+ end
166
+ total = error + not_applicable + not_reviewed + failed + passed
167
+ enhanced_outcomes_summary = {
168
+ not_applicable: {
169
+ total: not_applicable,
170
+ },
171
+ not_reviewed: {
172
+ total: not_reviewed,
173
+ },
174
+ error: {
175
+ total: error,
176
+ },
177
+ }
178
+ else
179
+ all_unique_controls.each do |control|
180
+ next unless control[:results]
140
181
 
141
- {
182
+ if control[:results].any? { |r| r[:status] == "failed" }
183
+ failed += 1
184
+ elsif control[:results].any? { |r| r[:status] == "skipped" }
185
+ skipped += 1
186
+ else
187
+ passed += 1
188
+ end
189
+ end
190
+ total = failed + passed + skipped
191
+ end
192
+ final_summary = {
142
193
  total: total,
143
194
  passed: {
144
195
  total: passed,
@@ -150,6 +201,8 @@ module Inspec::Formatters
150
201
  total: failed,
151
202
  },
152
203
  }
204
+
205
+ final_summary.merge!(enhanced_outcomes_summary)
153
206
  end
154
207
 
155
208
  def exception_message(exception)