From 6d23f817d045a545061e7b1e97e0812718394b22 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 13 Jun 2022 13:51:58 -0400 Subject: [PATCH 01/93] Add inspec-6 branch as release branch Signed-off-by: Clinton Wolfe --- .expeditor/config.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.expeditor/config.yml b/.expeditor/config.yml index fbd986558..e00a581c6 100644 --- a/.expeditor/config.yml +++ b/.expeditor/config.yml @@ -77,13 +77,12 @@ github: - "Expeditor: Bump Major Version" release_branches: + - inspec-6: + version_constraint: 6.* - main: version_constraint: 5.* - inspec-4: version_constraint: 4.* - # We need to ensure all configs are in place to appropriately support this branch - # - expeditor-development: - # version_constraint: 4.* changelog: categories: From 87ca2b0aa7bee630ece212f41c4201cc65109ef4 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 13 Jun 2022 17:55:39 +0000 Subject: [PATCH 02/93] Bump version to 5.18.4 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6db848e5..b91bcce3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.3](https://github.com/inspec/inspec/tree/v5.18.3) (2022-06-10) + +## [v5.18.4](https://github.com/inspec/inspec/tree/v5.18.4) (2022-06-13) #### Merged Pull Requests -- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) +- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) - Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105)) - CFINSPEC-291: Fix `processes` resource to consider processes without `path` on Windows [#6100](https://github.com/inspec/inspec/pull/6100) ([ahasunos](https://github.com/ahasunos)) diff --git a/VERSION b/VERSION index 94bdd01c8..ece890387 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.3 \ No newline at end of file +5.18.4 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 9db8afb4c..3157cf430 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.3".freeze + VERSION = "5.18.4".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index cebb2b952..6f8449a20 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.3".freeze + VERSION = "5.18.4".freeze end From ffbf2ff5547c96f4982eccebc9630322879f9cac Mon Sep 17 00:00:00 2001 From: jayashrig158 <61735743+jayashrig158@users.noreply.github.com> Date: Mon, 13 Jun 2022 23:54:33 +0530 Subject: [PATCH 03/93] add ruby test 3.1 in verify pipeline (#5892) * add ruby test 3.1 in verify pipeline Signed-off-by: jayashri garud * updating rubygem to 3.3.3 Signed-off-by: jayashri garud * Test fixture plugin files for ABI 3.1.0 Signed-off-by: Clinton Wolfe * Use kwargs to match new calling style for ERB in ruby 2.7 and 3.0 Signed-off-by: Clinton Wolfe * Allow Date and Time data types, needed for waivers, since safe load is now the default for YAML Signed-off-by: Clinton Wolfe * Use Time.parse, not Time.new since the rules tightened for what you can pass the constructor for waiver dates Signed-off-by: Clinton Wolfe * Add ruby platform debug message to test parallel output Signed-off-by: Clinton Wolfe * Conditionalize permitted_classes in inputs yaml_load for <= 3.1.x only Signed-off-by: Clinton Wolfe * In ruby 3.1.0 net/pop3 is no more a default gem and that will require us to add it in Gemfile so trying with the byebug gem which we are bundling already Signed-off-by: Vasu1105 * CFINSPEC-166 Updated url fetcher to handle Net::OpenTimeout Exception on Windows Signed-off-by: Vasu1105 Co-authored-by: Clinton Wolfe Co-authored-by: Vasu1105 --- .expeditor/buildkite/verify.sh | 2 +- .expeditor/verify.pipeline.yml | 27 +++ .github/CODEOWNERS | 2 +- Rakefile | 3 +- lib/inspec/fetcher/url.rb | 2 +- lib/inspec/rule.rb | 2 +- lib/inspec/secrets/yaml.rb | 8 +- .../templates/body.html.erb | 8 +- .../templates/control.html.erb | 2 +- .../templates/profile.html.erb | 2 +- .../inspec-test-fixture.gemspec | 29 +++ .../lib/inspec-test-fixture.rb | 5 + .../lib/inspec-test-fixture/mock_plugin.rb | 13 ++ .../lib/inspec-test-fixture/plugin.rb | 13 ++ .../lib/inspec-test-fixture/version.rb | 5 + .../inspec-test-fixture-0.1.0.gemspec | 30 +++ .../inspec-test-fixture.gemspec | 29 +++ .../lib/inspec-test-fixture.rb | 5 + .../lib/inspec-test-fixture/mock_plugin.rb | 13 ++ .../lib/inspec-test-fixture/plugin.rb | 13 ++ .../lib/inspec-test-fixture/version.rb | 5 + .../gems/ordinal_array-0.2.0/README.rdoc | 29 +++ .../ordinal_array-0.2.0/lib/ordinal_array.rb | 65 ++++++ .../lib/ordinal_array/ordinal.rb | 71 +++++++ .../lib/ordinal_array/ordinal_constants.rb | 84 ++++++++ .../ordinal_array-0.2.0/ordinal_array.gemspec | 18 ++ .../inspec-test-fixture-0.2.0.gemspec | 33 +++ .../ordinal_array-0.2.0.gemspec | 18 ++ .../gems/train-test-fixture-0.1.0/LICENSE | 201 ++++++++++++++++++ .../gems/train-test-fixture-0.1.0/README.md | 5 + .../lib/train-test-fixture.rb | 4 + .../lib/train-test-fixture/connection.rb | 39 ++++ .../lib/train-test-fixture/platform.rb | 18 ++ .../lib/train-test-fixture/transport.rb | 14 ++ .../lib/train-test-fixture/version.rb | 5 + .../train-test-fixture.gemspec | 34 +++ .../train-test-fixture-0.1.0.gemspec | 19 ++ test/unit/profiles/profile_context_test.rb | 14 +- 38 files changed, 874 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/inspec-test-fixture.gemspec create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture.rb create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/mock_plugin.rb create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/plugin.rb create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/version.rb create mode 100644 test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/specifications/inspec-test-fixture-0.1.0.gemspec create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/inspec-test-fixture.gemspec create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/mock_plugin.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/plugin.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/version.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/README.rdoc create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal_constants.rb create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/ordinal_array.gemspec create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/inspec-test-fixture-0.2.0.gemspec create mode 100644 test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/ordinal_array-0.2.0.gemspec create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/LICENSE create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/README.md create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture.rb create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/connection.rb create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/platform.rb create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/transport.rb create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/version.rb create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/train-test-fixture.gemspec create mode 100644 test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/specifications/train-test-fixture-0.1.0.gemspec diff --git a/.expeditor/buildkite/verify.sh b/.expeditor/buildkite/verify.sh index d30de3ff6..551e52cd3 100755 --- a/.expeditor/buildkite/verify.sh +++ b/.expeditor/buildkite/verify.sh @@ -10,7 +10,7 @@ useradd -m -U --uid 2000 normal echo "normal ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/normal echo "--- updating rubygems" -gem update -N --system 3.2.3 --force +gem update -N --system 3.3.3 --force echo "--- system details" uname -a diff --git a/.expeditor/verify.pipeline.yml b/.expeditor/verify.pipeline.yml index f6c4aae90..d62f73964 100644 --- a/.expeditor/verify.pipeline.yml +++ b/.expeditor/verify.pipeline.yml @@ -33,6 +33,13 @@ steps: docker: image: ruby:3.0 + - label: run-tests-ruby-3.1 + command: + - /workdir/.expeditor/buildkite/verify.sh + expeditor: + executor: + docker: + image: ruby:3.1 - label: isolated-tests-ruby-3.0 command: @@ -42,6 +49,14 @@ steps: docker: image: ruby:3.0 + - label: isolated-tests-ruby-3.1 + command: + - RAKE_TASK=test:isolated /workdir/.expeditor/buildkite/verify.sh + expeditor: + executor: + docker: + image: ruby:3.1 + - label: run-tests-ruby-3.0-windows command: - /workdir/.expeditor/buildkite/verify.ps1 @@ -53,3 +68,15 @@ steps: host_os: windows shell: ["powershell", "-Command"] image: rubydistros/windows-2019:3.0 + + - label: run-tests-ruby-3.1-windows + command: + - /workdir/.expeditor/buildkite/verify.ps1 + expeditor: + executor: + docker: + environment: + - BUILDKITE + host_os: windows + shell: ["powershell", "-Command"] + image: rubydistros/windows-2019:3.1 \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b41bd5adb..42277e173 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,4 +2,4 @@ * @inspec/inspec-core-team docs-chef-io/** @inspec/inspec-core-team @inspec/docs-team -*.md @inspec/inspec-core-team @inspec/docs-team +*.md @inspec/inspec-core-team @inspec/docs-team \ No newline at end of file diff --git a/Rakefile b/Rakefile index 76dd166a2..dcf587c1a 100755 --- a/Rakefile +++ b/Rakefile @@ -107,6 +107,7 @@ namespace :test do task :parallel do n = (ENV["K"] || 4).to_i + warn "Have RUBY_PLATFORM as #{RUBY_PLATFORM}" lock = Mutex.new passed = true @@ -330,4 +331,4 @@ end desc "Show the version of this gem" task :version do inspec_version -end \ No newline at end of file +end diff --git a/lib/inspec/fetcher/url.rb b/lib/inspec/fetcher/url.rb index b6f198457..18628cb0a 100644 --- a/lib/inspec/fetcher/url.rb +++ b/lib/inspec/fetcher/url.rb @@ -262,7 +262,7 @@ module Inspec::Fetcher open(target, opts) - rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e + rescue SocketError, Net::OpenTimeout, Errno::ECONNREFUSED, OpenURI::HTTPError => e raise Inspec::FetcherFailure, "Profile URL dependency #{target} could not be fetched: #{e.message}" end diff --git a/lib/inspec/rule.rb b/lib/inspec/rule.rb index 9c20b2119..26b302b4c 100644 --- a/lib/inspec/rule.rb +++ b/lib/inspec/rule.rb @@ -358,7 +358,7 @@ module Inspec # YAML will automagically give us a Date or a Time. # If transcoding YAML between languages (e.g. Go) the date might have also ended up as a String. # A string that does not represent a valid time results in the date 0000-01-01. - if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.new(expiry).year != 0) + if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.parse(expiry).year != 0) expiry = expiry.to_time if expiry.is_a? Date expiry = Time.parse(expiry) if expiry.is_a? String if expiry < Time.now # If the waiver expired, return - no skip applied diff --git a/lib/inspec/secrets/yaml.rb b/lib/inspec/secrets/yaml.rb index 4d9d1a1e5..19bb52731 100644 --- a/lib/inspec/secrets/yaml.rb +++ b/lib/inspec/secrets/yaml.rb @@ -16,7 +16,13 @@ module Secrets # array of yaml file paths def initialize(target) - @inputs = ::YAML.load_file(target) + # Ruby 3.1 treats YAML load as a dangerous operation by default, requiring us to declare date and time classes as permitted + # It's not a valid option in 3.0.x + if Gem.ruby_version >= Gem::Version.new("3.1.0") + @inputs = ::YAML.load_file(target, permitted_classes: [Date, Time]) + else + @inputs = ::YAML.load_file(target) + end if @inputs == false || !@inputs.is_a?(Hash) Inspec::Log.warn("#{self.class} unable to parse #{target}: invalid YAML or contents is not a Hash") diff --git a/lib/plugins/inspec-reporter-html2/templates/body.html.erb b/lib/plugins/inspec-reporter-html2/templates/body.html.erb index 695d26d9a..daa827abf 100644 --- a/lib/plugins/inspec-reporter-html2/templates/body.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/body.html.erb @@ -7,21 +7,21 @@ - <%= ERB.new(File.read(template_path + "/selector.html.erb"), nil, nil, "_select").result(binding) %> + <%= ERB.new(File.read(template_path + "/selector.html.erb"), eoutvar: "_select").result(binding) %>

<%= Inspec::Dist::PRODUCT_NAME %> Report

<% run_data.profiles.each do |profile| %> - <%= ERB.new(File.read(template_path + "/profile.html.erb"), nil, nil, "_prof").result(binding) %> + <%= ERB.new(File.read(template_path + "/profile.html.erb"), eoutvar: "_prof").result(binding) %> <% end %>
diff --git a/lib/plugins/inspec-reporter-html2/templates/control.html.erb b/lib/plugins/inspec-reporter-html2/templates/control.html.erb index 33ecb854c..b011649be 100644 --- a/lib/plugins/inspec-reporter-html2/templates/control.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/control.html.erb @@ -74,7 +74,7 @@ <% control.results.each do |result| %> - <%= ERB.new(File.read(template_path + "/result.html.erb"), nil, nil, "_rslt").result(binding) %> + <%= ERB.new(File.read(template_path + "/result.html.erb"), eoutvar: "_rslt").result(binding) %> <% end %>
diff --git a/lib/plugins/inspec-reporter-html2/templates/profile.html.erb b/lib/plugins/inspec-reporter-html2/templates/profile.html.erb index 727e99d05..ca3aa3304 100644 --- a/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/profile.html.erb @@ -18,7 +18,7 @@ <% if profile.status == "loaded" %> <% profile.controls.each do |control| %> - <%= ERB.new(File.read(template_path + "/control.html.erb"), nil, nil, "_ctl").result(binding) %> + <%= ERB.new(File.read(template_path + "/control.html.erb"), eoutvar: "_ctl").result(binding) %> <% end %> <% end %>
diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/inspec-test-fixture.gemspec b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/inspec-test-fixture.gemspec new file mode 100644 index 000000000..3e3c71eb9 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/inspec-test-fixture.gemspec @@ -0,0 +1,29 @@ +lib = File.expand_path("lib", __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "inspec-test-fixture/version" + +Gem::Specification.new do |spec| + spec.name = "inspec-test-fixture" + spec.version = InspecPlugins::TestFixture::VERSION + spec.authors = ["InSpec Engineering Team"] + spec.email = ["hello@chef.io"] + + spec.summary = %q{A simple test plugin gem for InSpec} + spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.} + spec.homepage = "https://github.com/inspec/inspec" + + spec.files = [ + "inspec-test-fixture.gemspec", + "lib/inspec-test-fixture.rb", + "lib/inspec-test-fixture/plugin.rb", + "lib/inspec-test-fixture/mock_plugin.rb", + "lib/inspec-test-fixture/version.rb", + ] + spec.executables = [] + spec.require_paths = ["lib"] + + spec.add_development_dependency "rake", "~> 10.0" + if InspecPlugins::TestFixture::VERSION == "0.2.0" + spec.add_dependency "ordinal_array", "~> 0.2.0" + end +end diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture.rb b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture.rb new file mode 100644 index 000000000..e8b88aa91 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture.rb @@ -0,0 +1,5 @@ +lib = File.expand_path("../../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +require_relative 'inspec-test-fixture/version' +require_relative 'inspec-test-fixture/plugin' diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/mock_plugin.rb b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/mock_plugin.rb new file mode 100644 index 000000000..c6b162353 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/mock_plugin.rb @@ -0,0 +1,13 @@ +require 'inspec-test-fixture/version' +if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0') + require "ordinal_array" +end + +module InspecPlugins::TextFixture + class MockPlugin < Inspec.plugin(2, :mock_plugin_type) + def execute(opts = {}) + # Check to see if Array responds to 'third' + Array.respond_to?(:third) + end + end +end \ No newline at end of file diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/plugin.rb b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/plugin.rb new file mode 100644 index 000000000..6bb0136ff --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/plugin.rb @@ -0,0 +1,13 @@ +module InspecPlugins + module TestFixture + + class Plugin < Inspec.plugin(2) + plugin_name :'inspec-test-fixture' + + mock_plugin_type :'inspec-test-fixture' do + require_relative 'mock_plugin' + InspecPlugins::TestFixture + end + end + end +end \ No newline at end of file diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/version.rb b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/version.rb new file mode 100644 index 000000000..fd1e59656 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/gems/inspec-test-fixture-0.1.0/lib/inspec-test-fixture/version.rb @@ -0,0 +1,5 @@ +module InspecPlugins + module TestFixture + VERSION = "0.1.0" + end +end diff --git a/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/specifications/inspec-test-fixture-0.1.0.gemspec b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/specifications/inspec-test-fixture-0.1.0.gemspec new file mode 100644 index 000000000..4f5168e96 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-1-float/gems/3.1.0/specifications/inspec-test-fixture-0.1.0.gemspec @@ -0,0 +1,30 @@ +# stub: inspec-test-fixture 0.1.0 ruby lib + +Gem::Specification.new do |s| + s.name = "inspec-test-fixture".freeze + s.version = "0.1.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["InSpec Engineering Team".freeze] + s.date = "2019-04-24" + s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze + s.email = ["hello@chef.io".freeze] + s.homepage = "https://github.com/inspec/inspec".freeze + s.rubygems_version = "3.0.3".freeze + s.summary = "A simple test plugin gem for InSpec".freeze + + s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version + + if s.respond_to? :specification_version + s.specification_version = 4 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.2.0") + s.add_development_dependency(%q{rake}.freeze, ["~> 10.0"]) + else + s.add_dependency(%q{rake}.freeze, ["~> 10.0"]) + end + else + s.add_dependency(%q{rake}.freeze, ["~> 10.0"]) + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/inspec-test-fixture.gemspec b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/inspec-test-fixture.gemspec new file mode 100644 index 000000000..3e3c71eb9 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/inspec-test-fixture.gemspec @@ -0,0 +1,29 @@ +lib = File.expand_path("lib", __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "inspec-test-fixture/version" + +Gem::Specification.new do |spec| + spec.name = "inspec-test-fixture" + spec.version = InspecPlugins::TestFixture::VERSION + spec.authors = ["InSpec Engineering Team"] + spec.email = ["hello@chef.io"] + + spec.summary = %q{A simple test plugin gem for InSpec} + spec.description = %q{This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.} + spec.homepage = "https://github.com/inspec/inspec" + + spec.files = [ + "inspec-test-fixture.gemspec", + "lib/inspec-test-fixture.rb", + "lib/inspec-test-fixture/plugin.rb", + "lib/inspec-test-fixture/mock_plugin.rb", + "lib/inspec-test-fixture/version.rb", + ] + spec.executables = [] + spec.require_paths = ["lib"] + + spec.add_development_dependency "rake", "~> 10.0" + if InspecPlugins::TestFixture::VERSION == "0.2.0" + spec.add_dependency "ordinal_array", "~> 0.2.0" + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture.rb new file mode 100644 index 000000000..e8b88aa91 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture.rb @@ -0,0 +1,5 @@ +lib = File.expand_path("../../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +require_relative 'inspec-test-fixture/version' +require_relative 'inspec-test-fixture/plugin' diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/mock_plugin.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/mock_plugin.rb new file mode 100644 index 000000000..c6b162353 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/mock_plugin.rb @@ -0,0 +1,13 @@ +require 'inspec-test-fixture/version' +if InspecPlugins::TestFixture::VERSION == Gem::Version.new('0.2.0') + require "ordinal_array" +end + +module InspecPlugins::TextFixture + class MockPlugin < Inspec.plugin(2, :mock_plugin_type) + def execute(opts = {}) + # Check to see if Array responds to 'third' + Array.respond_to?(:third) + end + end +end \ No newline at end of file diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/plugin.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/plugin.rb new file mode 100644 index 000000000..6bb0136ff --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/plugin.rb @@ -0,0 +1,13 @@ +module InspecPlugins + module TestFixture + + class Plugin < Inspec.plugin(2) + plugin_name :'inspec-test-fixture' + + mock_plugin_type :'inspec-test-fixture' do + require_relative 'mock_plugin' + InspecPlugins::TestFixture + end + end + end +end \ No newline at end of file diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/version.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/version.rb new file mode 100644 index 000000000..8b4c3984e --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/inspec-test-fixture-0.2.0/lib/inspec-test-fixture/version.rb @@ -0,0 +1,5 @@ +module InspecPlugins + module TestFixture + VERSION = "0.2.0" + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/README.rdoc b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/README.rdoc new file mode 100644 index 000000000..0c5316c1f --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/README.rdoc @@ -0,0 +1,29 @@ += Ordinal array + +Ordinal array is a Gem which allows you to access to a value of an array by an ordinal value. You can access to the first element of an array by the "first" method. Now you can access to the second element by "second", the third by "third" and that until the 999th elements. + +Works with Ruby 1.9 + +Improve ordinal array speed for multiple calls on the same method on the same array: (Benchmark results: https://gist.github.com/2337544) + +Exemple: + my_array = ["value1", "value2", "value3"] + puts my_array.third # print: value 3 + puts my_array.third # print: value 3 (But... much faster!) + += How to use + +Just add gem "ordinal_array" in your gemfile. +It provides you some methods on the basic array class + +Exemple of use: + + my_array = ["value1", "value2", "value3"] + puts my_array.third # print: value 3 + my_array[41] = "it works fine" + puts my_array.fourty_second # print: it works fine + += Contributors + +*Kevin Disneur + diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array.rb new file mode 100644 index 000000000..4ca08596c --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array.rb @@ -0,0 +1,65 @@ +require_relative './ordinal_array/ordinal' +require_relative './ordinal_array/ordinal_constants' + +class Array + + include OrdinalArray::Constant + include OrdinalArray + + def self.respond_to?(method_sym, include_private=false) + return true if Array.number_in_letter? method_sym + super + end + + def method_missing(name, *params) + if Array.number_in_letter? name + index = index_by_number_in_letter(name, params) + + self.class.send(:define_method, name) do + self[index] + end + + self.send(name) + else + super + end + end + + private + + def self.number_in_letter?(name) + ordinal_figure = false + possible_followers = [:hundred, :decade, :ordinal] + + letter_numbers = name.to_s.split('_').drop_while do |letter_number| + return false if ordinal_figure + + figure = Numbers_in_letter.element_by_name(letter_number) + return false unless figure + return false unless possible_followers.include? figure.to_sym + + possible_followers = figure.can_be_followed_by + ordinal_figure = !figure.kind_of?(ComposedOrdinal) + + true + end + letter_numbers.empty? && ordinal_figure + end + + def index_by_number_in_letter(name, *params) + partial_sum = 1 + sum = name.to_s.split('_').inject(0) do |sum, letter_number| + number = Numbers_in_letter.element_by_name(letter_number).number + if partial_sum < number + partial_sum = partial_sum * number + else + sum = sum + partial_sum + partial_sum = number + end + sum + end + sum = sum + partial_sum + index = sum - 1 + index > 0 ? index : nil + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal.rb new file mode 100644 index 000000000..74c91cc35 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal.rb @@ -0,0 +1,71 @@ +module OrdinalArray + + class Ordinal + attr_accessor :number_in_letter, :number, :can_be_followed_by + + def initialize(number_in_letter, number) + @number_in_letter = number_in_letter + @can_be_followed_by = nil + @number = number + end + + def to_sym + :ordinal + end + end + + class ComposedOrdinal < Ordinal + def initialize(number_in_letter, number) + super(number_in_letter, number) + @can_be_followed_by = [:hundred] + end + + def to_sym + :ordinal + end + end + + class DecadeOrdinal < Ordinal + def initialize(number_in_letter, number) + super(number_in_letter, number) + @can_be_followed_by = nil + end + + def to_sym + :decade + end + end + + class ComposedDecadeOrdinal < ComposedOrdinal + def initialize(number_in_letter, number) + super(number_in_letter, number) + @can_be_followed_by = [:ordinal] + end + + def to_sym + :decade + end + end + + class HundredOrdinal < Ordinal + def initialize(number_in_letter, number) + super(number_in_letter, number) + @can_be_followed_by = nil + end + + def to_sym + :hundred + end + end + + class ComposedHundredOrdinal < ComposedDecadeOrdinal + def initialize(number_in_letter, number) + super(number_in_letter, number) + @can_be_followed_by = [:decade, :ordinal] + end + + def to_sym + :hundred + end + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal_constants.rb b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal_constants.rb new file mode 100644 index 000000000..5ab142e98 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/lib/ordinal_array/ordinal_constants.rb @@ -0,0 +1,84 @@ +require_relative './ordinal' + +module OrdinalArray + module Constant + + First = Ordinal.new("first", 1) + One = ComposedOrdinal.new("one", 1) + Second = Ordinal.new("second", 2) + Two = ComposedOrdinal.new("two", 2) + Third = Ordinal.new("third", 3) + Three = ComposedOrdinal.new("three", 3) + Fourth = Ordinal.new("fourth", 4) + Four = ComposedOrdinal.new("four", 4) + Fifth = Ordinal.new("fifth", 5) + Five = ComposedOrdinal.new("five", 5) + Sixth = Ordinal.new("sixth", 6) + Six = ComposedOrdinal.new("six", 6) + Seventh = Ordinal.new("seventh", 7) + Seven = ComposedOrdinal.new("seven", 7) + Eighth = Ordinal.new("eighth", 8) + Eight = ComposedOrdinal.new("eight", 8) + Ninth = Ordinal.new("ninth", 9) + Nine = ComposedOrdinal.new("nine", 9) + Tenth = DecadeOrdinal.new("tenth", 10) + Ten = ComposedDecadeOrdinal.new("ten", 10) + Eleventh = DecadeOrdinal.new("eleventh", 11) + Eleven = ComposedDecadeOrdinal.new("eleven", 11) + Twelfth = DecadeOrdinal.new("twelfth", 12) + Twelve = ComposedDecadeOrdinal.new("twelve", 12) + Thirteenth = DecadeOrdinal.new("thirteenth", 13) + Thirteen = ComposedDecadeOrdinal.new("thirteen", 13) + Fourteenth = DecadeOrdinal.new("fourteenth", 14) + Fourteen = ComposedDecadeOrdinal.new("fourteen", 14) + Fifteenth = DecadeOrdinal.new("fifteenth", 15) + Fifteen = ComposedDecadeOrdinal.new("fifteen", 15) + Sixteenth = DecadeOrdinal.new("sixteenth", 16) + Sixteen = ComposedDecadeOrdinal.new("sixteen", 16) + Seventeenth = DecadeOrdinal.new("seventeenth", 17) + Seventeen = ComposedDecadeOrdinal.new("seventeen", 17) + Eighteenth = DecadeOrdinal.new("eighteenth", 18) + Eighteen = ComposedDecadeOrdinal.new("eighteen", 18) + Nineteenth = DecadeOrdinal.new("nineteenth", 19) + Nineteen = ComposedDecadeOrdinal.new("nineteen", 19) + Twentieth = DecadeOrdinal.new("twentieth", 20) + Twenty = ComposedDecadeOrdinal.new("twenty", 20) + Thirtieth = DecadeOrdinal.new("thirtieth", 30) + Thirty = ComposedDecadeOrdinal.new("thirty", 30) + Fortieth = DecadeOrdinal.new("fortieth", 40) + Fourty = ComposedDecadeOrdinal.new("fourty", 40) + Fiftieth = DecadeOrdinal.new("fiftieth", 50) + Fifty = ComposedDecadeOrdinal.new("fifty", 50) + Sixtieth = DecadeOrdinal.new("sixtieth", 60) + Sixty = ComposedDecadeOrdinal.new("sixty", 60) + Seventieth = DecadeOrdinal.new("seventieth", 70) + Seventy = ComposedDecadeOrdinal.new("seventy", 70) + Eightieth = DecadeOrdinal.new("eightieth", 80) + Eighty = ComposedDecadeOrdinal.new("eighty", 80) + Ninetieth = DecadeOrdinal.new("ninetieth", 90) + Ninety = ComposedDecadeOrdinal.new("ninety", 90) + Hundredth = HundredOrdinal.new("hundredth", 100) + Hundred = ComposedHundredOrdinal.new("hundred", 100) + + Numbers_in_letter = [ + One, Two, Three, Four, Five, Six, Seven, Eight, Nine, + Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen, + Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety, + Hundred, + + First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, + Tenth, Eleventh, Twelfth, Thirteenth, Fourteenth, Fifteenth, Sixteenth, Seventeenth, Eighteenth, Nineteenth, + Twentieth, Thirtieth, Fortieth, Fiftieth, Sixtieth, Seventieth, Eightieth, Ninetieth, + Hundredth + ] + + class << Numbers_in_letter + def element_by_name(name) + index = self.index {|n| n.number_in_letter == name } + return nil unless index + self.[](index) + end + end + + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/ordinal_array.gemspec b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/ordinal_array.gemspec new file mode 100644 index 000000000..51fc008ea --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/gems/ordinal_array-0.2.0/ordinal_array.gemspec @@ -0,0 +1,18 @@ +# stub: ordinal_array 0.2.0 ruby lib + +Gem::Specification.new do |s| + s.name = "ordinal_array".freeze + s.version = "0.2.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Kevin Disneur".freeze] + s.date = "2012-04-08" + s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze + s.email = ["kevin.disneur@gmail.com".freeze] + s.homepage = "https://github.com/kdisneur/ordinal_array".freeze + s.rubygems_version = "3.0.3".freeze + s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze + + s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/inspec-test-fixture-0.2.0.gemspec b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/inspec-test-fixture-0.2.0.gemspec new file mode 100644 index 000000000..5884e66b7 --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/inspec-test-fixture-0.2.0.gemspec @@ -0,0 +1,33 @@ +# stub: inspec-test-fixture 0.2.0 ruby lib + +Gem::Specification.new do |s| + s.name = "inspec-test-fixture".freeze + s.version = "0.2.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["InSpec Engineering Team".freeze] + s.date = "2019-04-24" + s.description = "This gem is used to test the gem search and install capabilities of InSpec's plugin V2 system. It is not a good example or starting point for plugin development.".freeze + s.email = ["hello@chef.io".freeze] + s.homepage = "https://github.com/inspec/inspec".freeze + s.rubygems_version = "3.0.3".freeze + s.summary = "A simple test plugin gem for InSpec".freeze + + s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version + + if s.respond_to? :specification_version + s.specification_version = 4 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.2.0") + s.add_development_dependency(%q{rake}.freeze, ["~> 10.0"]) + s.add_runtime_dependency(%q{ordinal_array}.freeze, ["~> 0.2.0"]) + else + s.add_dependency(%q{rake}.freeze, ["~> 10.0"]) + s.add_dependency(%q{ordinal_array}.freeze, ["~> 0.2.0"]) + end + else + s.add_dependency(%q{rake}.freeze, ["~> 10.0"]) + s.add_dependency(%q{ordinal_array}.freeze, ["~> 0.2.0"]) + end +end diff --git a/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/ordinal_array-0.2.0.gemspec b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/ordinal_array-0.2.0.gemspec new file mode 100644 index 000000000..51fc008ea --- /dev/null +++ b/test/fixtures/config_dirs/test-fixture-2-float/gems/3.1.0/specifications/ordinal_array-0.2.0.gemspec @@ -0,0 +1,18 @@ +# stub: ordinal_array 0.2.0 ruby lib + +Gem::Specification.new do |s| + s.name = "ordinal_array".freeze + s.version = "0.2.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Kevin Disneur".freeze] + s.date = "2012-04-08" + s.description = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze + s.email = ["kevin.disneur@gmail.com".freeze] + s.homepage = "https://github.com/kdisneur/ordinal_array".freeze + s.rubygems_version = "3.0.3".freeze + s.summary = "You can access to the first element of an array by the 'first' method. Now you can access to the second element by 'second' and that until the 999th elements".freeze + + s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/LICENSE b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/LICENSE new file mode 100644 index 000000000..11069edd7 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/README.md b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/README.md new file mode 100644 index 000000000..c886cf2c0 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/README.md @@ -0,0 +1,5 @@ +This is a very simple train transport plugin. It provided fixed responses to file.content and command.stdout/stderr/exit_status. + +It is not a good example to use for learning, nor a good base for starting your own plugin - it's intended for for use during the testing of Train. + +For good examples of plugin development, see train/examples/plugin. \ No newline at end of file diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture.rb b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture.rb new file mode 100644 index 000000000..b5f5ed68b --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture.rb @@ -0,0 +1,4 @@ +lib = File.expand_path("../../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +require 'train-test-fixture/transport' \ No newline at end of file diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/connection.rb b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/connection.rb new file mode 100644 index 000000000..8d00de291 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/connection.rb @@ -0,0 +1,39 @@ +require 'train-test-fixture/platform' +require 'train/transports/local' + +module TrainPlugins + module TestFixture + class Connection < Train::Plugins::Transport::BaseConnection + include TrainPlugins::TestFixture::Platform + + def initialize(options) + super(options) + end + + def local? + true + end + + private + + def run_command_via_connection(cmd) + Train::Transports::Local::CommandResult.new( + 'Mock Command Result stdout', + 'Mock Command Result stderr', + 17 + ) + end + + def file_via_connection(path, *args) + MockFile.new(self, path) + end + + class MockFile < Train::File + def content + # Remarkably, the content is always the same. + 'Lorem Ipsum' + end + end + end + end +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/platform.rb b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/platform.rb new file mode 100644 index 000000000..fe2171c81 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/platform.rb @@ -0,0 +1,18 @@ +require 'train-test-fixture/version' + +module TrainPlugins + module TestFixture + module Platform + def platform + # Build this platform's family declarations. + # You'll need at least unix and windows to make the file() resource work. + Train::Platforms.name('test-fixture').in_family('unix') + Train::Platforms.name('test-fixture').in_family('windows') + force_platform!('test-fixture', + release: TrainPlugins::TestFixture::VERSION, + arch: 'mock', + ) + end + end + end +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/transport.rb b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/transport.rb new file mode 100644 index 000000000..c811209e6 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/transport.rb @@ -0,0 +1,14 @@ +require 'train-test-fixture/connection' + +module TrainPlugins + module TestFixture + class Transport < Train.plugin(1) + name 'test-fixture' + + def connection(_ = nil) + @connection ||= TrainPlugins::TestFixture::Connection.new(@options) + end + + end + end +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/version.rb b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/version.rb new file mode 100644 index 000000000..b68f3d9b6 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/lib/train-test-fixture/version.rb @@ -0,0 +1,5 @@ +module TrainPlugins + module TestFixture + VERSION = '0.1.0' + end +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/train-test-fixture.gemspec b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/train-test-fixture.gemspec new file mode 100644 index 000000000..608861784 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/gems/train-test-fixture-0.1.0/train-test-fixture.gemspec @@ -0,0 +1,34 @@ +lib = File.expand_path("lib", __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +Gem::Specification.new do |spec| + spec.name = "train-test-fixture" + spec.version = "0.1.0" + spec.authors = ["Inspec core engineering team"] + spec.email = ["hello@chef.io"] + spec.license = "Apache-2.0" + + spec.summary = %q{Test train plugin. Not intended for use as an example.} + spec.description = <<~EOD + Train plugin used in testing Train's plugin loader and InSpec's plugin manager. + This plugin does things that a normal plugin should not. Do not use it as an + example or as a starting point for plugin of your own. For that, please see + https://github.com/inspec/train/tree/master/examples/plugins + EOD + spec.homepage = "https://github.com/inspec/train" + + spec.files = %w{ + README.md + LICENSE + lib/train-test-fixture.rb + lib/train-test-fixture/version.rb + lib/train-test-fixture/transport.rb + lib/train-test-fixture/connection.rb + lib/train-test-fixture/platform.rb + train-test-fixture.gemspec + } + spec.executables = [] + spec.require_paths = ["lib"] + + # No deps +end diff --git a/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/specifications/train-test-fixture-0.1.0.gemspec b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/specifications/train-test-fixture-0.1.0.gemspec new file mode 100644 index 000000000..7ed593b57 --- /dev/null +++ b/test/fixtures/config_dirs/train-test-fixture/gems/3.1.0/specifications/train-test-fixture-0.1.0.gemspec @@ -0,0 +1,19 @@ +# stub: train-test-fixture 0.1.0 ruby lib + +Gem::Specification.new do |s| + s.name = "train-test-fixture".freeze + s.version = "0.1.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Inspec core engineering team".freeze] + s.date = "2018-09-26" + s.description = "Train plugin used in testing Train's plugin loader and InSpec's plugin manager.\nThis plugin does things that a normal plugin should not. Do not use it as an\nexample or as a starting point for plugin of your own. For that, please see\nhttps://github.com/inspec/train/tree/master/examples/plugins\n".freeze + s.email = ["hello@chef.io".freeze] + s.homepage = "https://github.com/inspec/train".freeze + s.licenses = ["Apache-2.0".freeze] + s.rubygems_version = "3.0.3".freeze + s.summary = "Test train plugin. Not intended for use as an example.".freeze + + s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version +end diff --git a/test/unit/profiles/profile_context_test.rb b/test/unit/profiles/profile_context_test.rb index a92ef2fe3..e19676da5 100644 --- a/test/unit/profiles/profile_context_test.rb +++ b/test/unit/profiles/profile_context_test.rb @@ -7,6 +7,8 @@ class Module include Minitest::Spec::DSL end +RUBY3_1_PLUS = Gem.ruby_version >= Gem::Version.new("3.1") + module DescribeOneTest it "loads an empty describe.one" do profile.load(format(context_format, "describe.one")) @@ -379,9 +381,15 @@ describe Inspec::ProfileContext do it "supports simple ruby require statements" do # Please note: we do discourage the use of Gems in inspec resources at # this time. Resources should be well packaged whenever possible. - _ { profile.load("Net::POP3") }.must_raise NameError - profile.load_libraries([['require "net/pop"', "libraries/a.rb"]]) - _(profile.load("Net::POP3").to_s).must_equal "Net::POP3" + if RUBY3_1_PLUS + _ { profile.load("Byebug") }.must_raise NameError + profile.load_libraries([['require "byebug"', "libraries/a.rb"]]) + _(profile.load("Byebug").to_s).must_equal "Byebug" + else + _ { profile.load("Net::POP3") }.must_raise NameError + profile.load_libraries([['require "net/pop"', "libraries/a.rb"]]) + _(profile.load("Net::POP3").to_s).must_equal "Net::POP3" + end end it "supports creating a simple library file (no require)" do From 19c23979e0aae1335b3349932ec116c7502d7ca5 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 13 Jun 2022 18:25:48 +0000 Subject: [PATCH 04/93] Bump version to 5.18.5 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b91bcce3a..04ca5e90e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.4](https://github.com/inspec/inspec/tree/v5.18.4) (2022-06-13) + +## [v5.18.5](https://github.com/inspec/inspec/tree/v5.18.5) (2022-06-13) #### Merged Pull Requests -- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) +- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) ### Changes since 5.17.4 release #### Merged Pull Requests +- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) - Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) - Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105)) diff --git a/VERSION b/VERSION index ece890387..734fd6426 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.4 \ No newline at end of file +5.18.5 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 3157cf430..5744867ed 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.4".freeze + VERSION = "5.18.5".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 6f8449a20..3a7cb49cf 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.4".freeze + VERSION = "5.18.5".freeze end From 8527d213cb3914e4421de0ee966e16be0c575f54 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 20 May 2022 19:19:33 +0530 Subject: [PATCH 05/93] Fix for dependent profiles with same name but different version to run and display Signed-off-by: Nikita Mathur --- lib/inspec/dependencies/dependency_set.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/inspec/dependencies/dependency_set.rb b/lib/inspec/dependencies/dependency_set.rb index 85f6ed62a..03978735a 100644 --- a/lib/inspec/dependencies/dependency_set.rb +++ b/lib/inspec/dependencies/dependency_set.rb @@ -39,7 +39,8 @@ module Inspec def self.flatten_dep_tree(dep_tree) dep_list = {} dep_tree.each do |d| - dep_list[d.name] = d + key_name = "#{d.name}-#{d.source_version}" + dep_list[key_name] = d dep_list.merge!(flatten_dep_tree(d.dependencies)) end dep_list From d43b1c2f436c69021c3764f27f48f649e15be99f Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 8 Jun 2022 17:53:26 +0530 Subject: [PATCH 06/93] Dsl using dependency with version to fetch dependency Signed-off-by: Nikita Mathur --- lib/inspec/dependencies/dependency_set.rb | 3 ++- lib/inspec/dsl.rb | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/inspec/dependencies/dependency_set.rb b/lib/inspec/dependencies/dependency_set.rb index 03978735a..1de5d76a2 100644 --- a/lib/inspec/dependencies/dependency_set.rb +++ b/lib/inspec/dependencies/dependency_set.rb @@ -25,7 +25,8 @@ module Inspec def self.from_array(dependencies, cwd, cache, backend) dep_list = {} dependencies.each do |d| - dep_list[d.name] = d + key_name = "#{d.name}-#{d.source_version}" + dep_list[key_name] = d end new(cwd, cache, dep_list, backend) end diff --git a/lib/inspec/dsl.rb b/lib/inspec/dsl.rb index d5af0e59b..be25e4b37 100644 --- a/lib/inspec/dsl.rb +++ b/lib/inspec/dsl.rb @@ -6,13 +6,13 @@ require "inspec/utils/deprecated_cloud_resources_list" module Inspec::DSL attr_accessor :backend - def require_controls(id, &block) - opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies } + def require_controls(id, version = nil, &block) + opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version } ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block) end - def include_controls(id, &block) - opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies } + def include_controls(id, version = nil, &block) + opts = { profile_id: id, include_all: true, backend: @backend, conf: @conf, dependencies: @dependencies, profile_version: version } ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block) end @@ -85,6 +85,19 @@ module Inspec::DSL def self.load_spec_files_for_profile(bind_context, opts, &block) dependencies = opts[:dependencies] profile_id = opts[:profile_id] + profile_version = opts[:profile_version] + + if profile_version + profile_id = "#{profile_id}-#{profile_version}" + else + profile_id_key = nil + dependencies.list.keys.each do |key| + profile_id_key = key.split("-") + profile_id_key.pop + profile_id_key = key if profile_id_key.join("-") == profile_id + end + profile_id = profile_id_key + end dep_entry = dependencies.list[profile_id] if dep_entry.nil? From 58d4a816f32dc3577000f0666390cedd4e0bc8e9 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 8 Jun 2022 19:47:20 +0530 Subject: [PATCH 07/93] Fix related to new profile id generation for fetching dependencies in dsl logic Signed-off-by: Nikita Mathur --- lib/inspec/dsl.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/inspec/dsl.rb b/lib/inspec/dsl.rb index be25e4b37..fc3d00f03 100644 --- a/lib/inspec/dsl.rb +++ b/lib/inspec/dsl.rb @@ -90,13 +90,13 @@ module Inspec::DSL if profile_version profile_id = "#{profile_id}-#{profile_version}" else - profile_id_key = nil + new_profile_id = nil dependencies.list.keys.each do |key| profile_id_key = key.split("-") profile_id_key.pop - profile_id_key = key if profile_id_key.join("-") == profile_id + new_profile_id = key if profile_id_key.join("-") == profile_id end - profile_id = profile_id_key + profile_id = new_profile_id end dep_entry = dependencies.list[profile_id] From 8e32d90349fd6dc1aefaeffb7afb12abc8f976c4 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 9 Jun 2022 11:54:52 +0530 Subject: [PATCH 08/93] Fix build issue Signed-off-by: Nikita Mathur --- lib/inspec/dependencies/dependency_set.rb | 4 ++-- lib/inspec/dsl.rb | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/inspec/dependencies/dependency_set.rb b/lib/inspec/dependencies/dependency_set.rb index 1de5d76a2..75d6d0ca3 100644 --- a/lib/inspec/dependencies/dependency_set.rb +++ b/lib/inspec/dependencies/dependency_set.rb @@ -25,7 +25,7 @@ module Inspec def self.from_array(dependencies, cwd, cache, backend) dep_list = {} dependencies.each do |d| - key_name = "#{d.name}-#{d.source_version}" + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : d.name) rescue d.name dep_list[key_name] = d end new(cwd, cache, dep_list, backend) @@ -40,7 +40,7 @@ module Inspec def self.flatten_dep_tree(dep_tree) dep_list = {} dep_tree.each do |d| - key_name = "#{d.name}-#{d.source_version}" + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : d.name) rescue d.name dep_list[key_name] = d dep_list.merge!(flatten_dep_tree(d.dependencies)) end diff --git a/lib/inspec/dsl.rb b/lib/inspec/dsl.rb index fc3d00f03..c42ffe6f4 100644 --- a/lib/inspec/dsl.rb +++ b/lib/inspec/dsl.rb @@ -96,9 +96,8 @@ module Inspec::DSL profile_id_key.pop new_profile_id = key if profile_id_key.join("-") == profile_id end - profile_id = new_profile_id end - dep_entry = dependencies.list[profile_id] + dep_entry = new_profile_id ? dependencies.list[new_profile_id] : dependencies.list[profile_id] if dep_entry.nil? raise <<~EOF From 70a48576114d5d43f300a97dc6eab9e01703ccee Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 9 Jun 2022 15:31:28 +0530 Subject: [PATCH 09/93] New profile id usage instead of profile_id in dsl Signed-off-by: Nikita Mathur --- lib/inspec/dsl.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inspec/dsl.rb b/lib/inspec/dsl.rb index c42ffe6f4..536d69954 100644 --- a/lib/inspec/dsl.rb +++ b/lib/inspec/dsl.rb @@ -87,10 +87,10 @@ module Inspec::DSL profile_id = opts[:profile_id] profile_version = opts[:profile_version] + new_profile_id = nil if profile_version - profile_id = "#{profile_id}-#{profile_version}" + new_profile_id = "#{profile_id}-#{profile_version}" else - new_profile_id = nil dependencies.list.keys.each do |key| profile_id_key = key.split("-") profile_id_key.pop From 1166489a93a3a55782c0fd586d2b0d43be4ddc6c Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 13 Jun 2022 20:26:14 +0530 Subject: [PATCH 10/93] Test case added for verifying running of same profile with different versions Signed-off-by: Nikita Mathur --- .../child-profile-1/controls/example.rb | 3 +++ .../inheritance/child-profile-1/inspec.yml | 15 +++++++++++++++ .../child-profile-2/controls/example.rb | 3 +++ .../inheritance/child-profile-2/inspec.yml | 14 ++++++++++++++ .../parent-profile/controls/example.rb | 2 ++ .../inheritance/parent-profile/inspec.yml | 15 +++++++++++++++ test/functional/inspec_exec_test.rb | 12 ++++++++++++ 7 files changed, 64 insertions(+) create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/inspec.yml create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/inspec.yml create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/parent-profile/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance/parent-profile/inspec.yml diff --git a/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/controls/example.rb new file mode 100644 index 000000000..58b8f3d12 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/controls/example.rb @@ -0,0 +1,3 @@ +require_controls "ssh" do + control "sshd-50" +end \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/inspec.yml new file mode 100644 index 000000000..a9decfbd9 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-1/inspec.yml @@ -0,0 +1,15 @@ +name: child-profile-1 +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: ssh + git: https://github.com/dev-sec/ssh-baseline.git + tag: 2.7.0 + \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/controls/example.rb new file mode 100644 index 000000000..8698d0387 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/controls/example.rb @@ -0,0 +1,3 @@ +require_controls "ssh" do + control "sshd-01" +end \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/inspec.yml new file mode 100644 index 000000000..230c76532 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/child-profile-2/inspec.yml @@ -0,0 +1,14 @@ +name: child-profile-2 +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: ssh + git: https://github.com/dev-sec/ssh-baseline.git + tag: 2.6.0 \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/controls/example.rb new file mode 100644 index 000000000..71ed7c9b3 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/controls/example.rb @@ -0,0 +1,2 @@ +include_controls "child-profile-1" +include_controls "child-profile-2" \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/inspec.yml new file mode 100644 index 000000000..da868a46f --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance/parent-profile/inspec.yml @@ -0,0 +1,15 @@ +name: parent-profile +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: child-profile-2 + path: ../child-profile-2 + - name: child-profile-1 + path: ../child-profile-1 \ No newline at end of file diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index d9b9e4fec..b8779edc5 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1305,4 +1305,16 @@ EOT assert_json_controls_passing end end + + describe "when profiles are dependent on different versions of same profile" do + let(:profile) { "#{profile_path}/git-fetcher/inheritance/parent-profile" } + let(:run_result) { run_inspec_process("exec #{profile}") } + it "should evaluate all test controls of all versions correctly" do + _(run_result.stderr).must_be_empty + _(run_result.stdout).must_include "2.7.0" + _(run_result.stdout).must_include "2.6.0" + _(run_result.stdout).must_include "sshd-01" + _(run_result.stdout).must_include "sshd-50" + end + end end From 465784a6b95f0cf344711f0d02654d11bb71a791 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 15 Jun 2022 14:41:42 +0530 Subject: [PATCH 11/93] Updated plugins doc with send_report functionality Signed-off-by: Nikita Mathur --- dev-docs/plugins.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dev-docs/plugins.md b/dev-docs/plugins.md index 5f80f8be9..0a0d7dfa2 100644 --- a/dev-docs/plugins.md +++ b/dev-docs/plugins.md @@ -493,6 +493,24 @@ v0.1.0 - Initial version v0.2.0 - added `run_data.profiles[0].inputs[0].options.sensitive` v0.3.0 - added resource_name && params +#### Implement send_report + +The primary responsibilty of this function is to implement a logic for sending reporter output through email invocations or making API calls. When this is defined in a reporter, rendering of output is skipped. + +```ruby +module InspecPlugins::Sweeten + class Reporter < Inspec.plugin(2, :reporter) + def send_report + # logic for sending reporter output using email invocations or API calls. + end + + def render + # this will be skipped, will only run send_report + end + end +end +``` + ## Implementing Streaming Reporter Plugins Streaming Reporter plugins offer the opportunity to customize or create a plugin which operates real-time as the Chef Inspec tests runs. Streaming reporters perform streaming using RSpec custom formatters. From 3094ea791ac757dfa7bdc4997cbe06418d510359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jun 2022 21:59:11 +0000 Subject: [PATCH 12/93] Bump octokit from 4.23.0 to 4.25.0 in /omnibus Bumps [octokit](https://github.com/octokit/octokit.rb) from 4.23.0 to 4.25.0. - [Release notes](https://github.com/octokit/octokit.rb/releases) - [Changelog](https://github.com/octokit/octokit.rb/blob/4-stable/RELEASE.md) - [Commits](https://github.com/octokit/octokit.rb/compare/v4.23.0...v4.25.0) --- updated-dependencies: - dependency-name: octokit dependency-type: indirect ... Signed-off-by: dependabot[bot] --- omnibus/Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index fdc9f733e..5c4688dde 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -278,7 +278,7 @@ GEM ffi-win32-extensions (~> 1.0.3) win32-process (~> 0.9) wmi-lite (~> 1.0) - mixlib-shellout (3.2.7-x64-mingw-ucrt) + mixlib-shellout (3.2.7-x64-unknown) chef-utils ffi-win32-extensions (~> 1.0.3) win32-process (~> 0.9) @@ -286,7 +286,7 @@ GEM mixlib-versioning (1.2.12) molinillo (0.8.0) multi_json (1.15.0) - multipart-post (2.2.0) + multipart-post (2.2.3) net-scp (3.0.0) net-ssh (>= 2.6.5, < 7.0.0) net-sftp (3.0.0) @@ -295,7 +295,7 @@ GEM net-ssh-gateway (2.0.0) net-ssh (>= 4.0.0) nori (2.6.0) - octokit (4.23.0) + octokit (4.25.0) faraday (>= 1, < 3) sawyer (~> 0.9) ohai (17.9.0) @@ -351,7 +351,7 @@ GEM ruby2_keywords (0.0.5) rubyntlm (0.6.3) rubyzip (2.3.2) - sawyer (0.9.1) + sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) semverse (3.0.2) From b611c9c2c8e1885ed71802eff586c7ca61e4acc3 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Sun, 19 Jun 2022 22:02:54 -0400 Subject: [PATCH 13/93] First pass at enhanced outcomes design doc Signed-off-by: Clinton Wolfe --- dev-docs/enhanced-outcomes.md | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 dev-docs/enhanced-outcomes.md diff --git a/dev-docs/enhanced-outcomes.md b/dev-docs/enhanced-outcomes.md new file mode 100644 index 000000000..ba03314b2 --- /dev/null +++ b/dev-docs/enhanced-outcomes.md @@ -0,0 +1,58 @@ +# Enhanced Outcomes + +Enhanced Outcomes refers to the addition of several new control outcomes to the InSpec vocabulary. + +## Test Outcomes vs Control Outcomes + +It is important to be clear that Enhanced Outcomes refers to new **control outcomes** - the results of running a control. The results of running a test - a `describe block` are determined by RSpec, and are limited to Pass, Fail, and Skip. Test outcomes are much more difficult to extend than control outcomes. + +## New Control Outcomes + +Enhanced Outcomes adds three new control outcomes to the existing Pass, Fail and Skip outcomes. + +### Error + +The Error outcome represents the situation when the system has encountered an error when attempting to evaluate the control code. But to a technical limitation, an accurate outcome is not possible to be obtained. + +In the first iteration of Enhanced Outcomes, Error outcome will be detected: + + * if the status of a test is Failed AND + * if the message of the test includes the text "Control source error" + +then the entire control should be marked Error. + +Additional means of detecting Error may be developed in the future. + +Error's abbreviation is ERR. + +Error's UI color assignment is Indigo. + +### Not Applicable + +If the control is not in Error and the impact of a control is 0.0, then the control's outcome is Not Applicable. + +Not Applicable's abbreviation is N/A. + +Not Applicable's UI color assignment is Sky Blue. + +### Not Reviewed + +If the control is not in Error or Not Applicable and all test results are Skipped, then the control outcome is Not Reviewed. + +Not Reviewed replaces Skipped as a control outcome. + +Not Reviewed's abbreviation is N/R. + +Not Reviewed's UI color assignment is Amber. + +## The --enhanced-outcomes option + +A new CLI option will be introduced for `inspec exec`, `inspec shell`, and `inspec schema` that controls the Enhanced Outcomes functionality. + +### InSpec 5 + +In InSpec 5.x, a user must request the enhanced outcomes functionality explicitly by adding the --enhanced-outcomes option. + +### InSpec 6 + +In InSpec 6.x, --enhanced-outcomes will default to true. A user may request disabling the enhanced outcomes functionality by adding the --no-enhanced-outcomes option. From 1d05d080c6e087d3540a7f6f77ea005030fe7bba Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Wed, 22 Jun 2022 16:34:30 +0000 Subject: [PATCH 14/93] Bump version to 5.18.6 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04ca5e90e..75d6deec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.5](https://github.com/inspec/inspec/tree/v5.18.5) (2022-06-13) + +## [v5.18.6](https://github.com/inspec/inspec/tree/v5.18.6) (2022-06-22) #### Merged Pull Requests -- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) +- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) - add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) - Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) diff --git a/VERSION b/VERSION index 734fd6426..72ee29089 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.5 \ No newline at end of file +5.18.6 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 5744867ed..3cdd69f4d 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.5".freeze + VERSION = "5.18.6".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 3a7cb49cf..73c7ce21c 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.5".freeze + VERSION = "5.18.6".freeze end From ad6c7f2c8f5e7ac4b1ca4d8a9aaf113569558d0b Mon Sep 17 00:00:00 2001 From: Vasu1105 Date: Thu, 23 Jun 2022 14:03:25 +0530 Subject: [PATCH 15/93] Adds --podman-url option accepts path to Podman API endpoint Signed-off-by: Vasu1105 --- lib/inspec/base_cli.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/inspec/base_cli.rb b/lib/inspec/base_cli.rb index b88486d92..f876af6f8 100644 --- a/lib/inspec/base_cli.rb +++ b/lib/inspec/base_cli.rb @@ -138,6 +138,8 @@ module Inspec desc: "Provides path to Docker API endpoint (Docker)" option :ssh_config_file, type: :array, desc: "A list of paths to the ssh config file, e.g ~/.ssh/config or /etc/ssh/ssh_config" + option :podman_url, type: :string, + desc: "Provides path to Podman API endpoint" end def self.profile_options From b744da071b2527fc394af2ed0576e62c62df76c8 Mon Sep 17 00:00:00 2001 From: Vasu1105 Date: Thu, 23 Jun 2022 14:54:03 +0530 Subject: [PATCH 16/93] Updated docs Signed-off-by: Vasu1105 --- README.md | 3 +++ docs-chef-io/content/inspec/cli.md | 6 ++++++ docs-chef-io/content/inspec/shell.md | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bae996de..4863774c9 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,9 @@ inspec exec test.rb -t winrm://Administrator@windowshost --password 'your-passwo # run test on docker container inspec exec test.rb -t docker://container_id +# run test on podman container +inspec exec test.rb -t podman://container_id --podman-url "unix:///run/user/1000/podman/podman.sock" + # run with sudo inspec exec test.rb --sudo [--sudo-password ...] [--sudo-options ...] [--sudo_command ...] diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index a48e418f0..318a016ed 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -137,6 +137,8 @@ This subcommand has the following additional options: Login path to use when connecting to the target (WinRM). * `-p`, `--port=N` Specify the login port for a remote scan. +* `--podman-url` + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--self-signed`, `--no-self-signed` @@ -343,6 +345,8 @@ This subcommand has the following additional options: Login path to use when connecting to the target (WinRM). * `-p`, `--port=N` Specify the login port for a remote scan. +* `--podman-url` + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--proxy-command=PROXY_COMMAND` @@ -559,6 +563,8 @@ This subcommand has the following additional options: Login path to use when connecting to the target (WinRM). * `-p`, `--port=N` Specify the login port for a remote scan. +* `--podman-url` + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` diff --git a/docs-chef-io/content/inspec/shell.md b/docs-chef-io/content/inspec/shell.md index f14449b4c..3212e9251 100644 --- a/docs-chef-io/content/inspec/shell.md +++ b/docs-chef-io/content/inspec/shell.md @@ -36,7 +36,7 @@ inspec help shell # This will describe inspec shell usage If you wish to connect to a remote machine (called a target within InSpec), you can use the `-t` flag. We support connecting using SSH, -WinRM and docker. If no target is provided, we implicitly support the +WinRM ,docker and podman. If no target is provided, we implicitly support the "local" target - i.e. tests running on the current machine running InSpec. For an SSH connection, use `-i` for specifying SSH key files, and the `--sudo*` commands for requesting a privilege escalation after @@ -49,6 +49,7 @@ inspec shell -t ssh://user@hostname:1234 -i /path/to/user_key # Login to hostna inspec shell -t winrm://UserName:Password@windowsmachine:1234 # Login to windowsmachine over WinRM as UserName. inspec shell -t winrm://windowsmachine --user 'UserName@domain' --password 'Secret123!' # Login to windowsmachine as UserName@domain.org. inspec shell -t docker://container_id # Login to a Docker container. +inspec shell -t podman://container_id --podman-url "unix:///run/user/$UID/podman/podman.sock" #Login to Podman rootless container. ``` ## Resource Packs From 1dc2faf2059217d7f0174c8306c56b3f497788ac Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Thu, 23 Jun 2022 18:33:23 +0530 Subject: [PATCH 17/93] Proof-read Signed-off-by: Deepa Kumaraswamy --- dev-docs/enhanced-outcomes.md | 50 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/dev-docs/enhanced-outcomes.md b/dev-docs/enhanced-outcomes.md index ba03314b2..514e7b9c1 100644 --- a/dev-docs/enhanced-outcomes.md +++ b/dev-docs/enhanced-outcomes.md @@ -1,58 +1,56 @@ # Enhanced Outcomes -Enhanced Outcomes refers to the addition of several new control outcomes to the InSpec vocabulary. +Enhanced Outcomes refers to the addition of new control outcomes to the InSpec vocabulary. -## Test Outcomes vs Control Outcomes +## Test Outcomes vs. Control Outcomes -It is important to be clear that Enhanced Outcomes refers to new **control outcomes** - the results of running a control. The results of running a test - a `describe block` are determined by RSpec, and are limited to Pass, Fail, and Skip. Test outcomes are much more difficult to extend than control outcomes. +It is essential to understand that Enhanced Outcomes refers to new **control outcomes**, the results of running a control. -## New Control Outcomes +The results of running a test, a `describe block` are determined by RSpec and are limited to `Pass`, `Fail`, and `Skip`. -Enhanced Outcomes adds three new control outcomes to the existing Pass, Fail and Skip outcomes. +Test outcomes are much more difficult to extend than control outcomes. + + + +Enhanced Outcomes adds three new control outcomes to the existing `Pass`, `Fail`, and `Skip` outcomes. ### Error -The Error outcome represents the situation when the system has encountered an error when attempting to evaluate the control code. But to a technical limitation, an accurate outcome is not possible to be obtained. +The **Error outcome** represents the situation when the system has encountered an error when attempting to evaluate the control code. However, due to a technical limitation, an accurate outcome cannot be obtained. -In the first iteration of Enhanced Outcomes, Error outcome will be detected: +In the first iteration of Enhanced Outcomes, the Error outcome is detected: - * if the status of a test is Failed AND - * if the message of the test includes the text "Control source error" +* if the status of a test is `Failed`, and +* if the message of the test includes the text, *Control source error* -then the entire control should be marked Error. +Then, the entire control should be marked `Error`. -Additional means of detecting Error may be developed in the future. +Additional means of detecting error may be developed in the future. -Error's abbreviation is ERR. - -Error's UI color assignment is Indigo. +Error's abbreviation is `ERR`. Error's UI color assignment is `Indigo`. ### Not Applicable -If the control is not in Error and the impact of a control is 0.0, then the control's outcome is Not Applicable. +If the control is not in `Error` and the impact of control is `0.0`, then the control's outcome is `Not Applicable`. -Not Applicable's abbreviation is N/A. - -Not Applicable's UI color assignment is Sky Blue. +Not Applicable's abbreviation is `N/A`. Not Applicable's UI color assignment is `Sky Blue`. ### Not Reviewed -If the control is not in Error or Not Applicable and all test results are Skipped, then the control outcome is Not Reviewed. +If the control is not in `Error` or `Not Applicable`, and all test results are `Skipped`, then the control outcome is `Not Reviewed`. -Not Reviewed replaces Skipped as a control outcome. +Not Reviewed replaces `Skipped` as a control outcome. -Not Reviewed's abbreviation is N/R. +Not Reviewed's abbreviation is `N/R`. Not Reviewed's UI color assignment is `Amber`. -Not Reviewed's UI color assignment is Amber. - -## The --enhanced-outcomes option +## The `--enhanced-outcomes` option A new CLI option will be introduced for `inspec exec`, `inspec shell`, and `inspec schema` that controls the Enhanced Outcomes functionality. ### InSpec 5 -In InSpec 5.x, a user must request the enhanced outcomes functionality explicitly by adding the --enhanced-outcomes option. +In InSpec 5.x, a user must request the enhanced outcomes functionality explicitly by adding the `--enhanced-outcomes` option. ### InSpec 6 -In InSpec 6.x, --enhanced-outcomes will default to true. A user may request disabling the enhanced outcomes functionality by adding the --no-enhanced-outcomes option. +In InSpec 6.x, --enhanced-outcomes will default to `true`. A user may request disabling the enhanced outcomes functionality by adding the `--no-enhanced-outcomes` option. From bcf888320c0686d001bee3cadfd8ec06ebabf481 Mon Sep 17 00:00:00 2001 From: Vasu1105 Date: Thu, 23 Jun 2022 19:28:49 +0530 Subject: [PATCH 18/93] Updated docs for --podman-url option Signed-off-by: Vasu1105 --- docs-chef-io/content/inspec/cli.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 318a016ed..5aafdc895 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -138,7 +138,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--self-signed`, `--no-self-signed` @@ -346,7 +346,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--proxy-command=PROXY_COMMAND` @@ -564,7 +564,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container. + Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` From b2e3bb342b12c559a8b64f7fa7a47dbd23de33e5 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 23 Jun 2022 19:57:36 +0530 Subject: [PATCH 19/93] using fake version 0.0.0 in case no source version available from a dep profile Signed-off-by: Nikita Mathur --- lib/inspec/dependencies/dependency_set.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inspec/dependencies/dependency_set.rb b/lib/inspec/dependencies/dependency_set.rb index 75d6d0ca3..e82f884b3 100644 --- a/lib/inspec/dependencies/dependency_set.rb +++ b/lib/inspec/dependencies/dependency_set.rb @@ -25,7 +25,7 @@ module Inspec def self.from_array(dependencies, cwd, cache, backend) dep_list = {} dependencies.each do |d| - key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : d.name) rescue d.name + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}-0.0.0") rescue "#{d.name}-0.0.0" dep_list[key_name] = d end new(cwd, cache, dep_list, backend) @@ -40,7 +40,7 @@ module Inspec def self.flatten_dep_tree(dep_tree) dep_list = {} dep_tree.each do |d| - key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : d.name) rescue d.name + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}-0.0.0") rescue "#{d.name}-0.0.0" dep_list[key_name] = d dep_list.merge!(flatten_dep_tree(d.dependencies)) end From fe8248bfb2535f2e1c7684ee17a6438b96656ff0 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 23 Jun 2022 10:28:25 -0400 Subject: [PATCH 20/93] Adjust criteria for Error Signed-off-by: Clinton Wolfe --- dev-docs/enhanced-outcomes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-docs/enhanced-outcomes.md b/dev-docs/enhanced-outcomes.md index ba03314b2..fe94532f1 100644 --- a/dev-docs/enhanced-outcomes.md +++ b/dev-docs/enhanced-outcomes.md @@ -16,8 +16,8 @@ The Error outcome represents the situation when the system has encountered an er In the first iteration of Enhanced Outcomes, Error outcome will be detected: - * if the status of a test is Failed AND - * if the message of the test includes the text "Control source error" + * if the message of any test includes the text "Control source error" OR + * the result of any test includes a backtrace then the entire control should be marked Error. From b203ca099b7ff52e849f01140e7dd2116e7ec039 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 23 Jun 2022 15:21:39 +0000 Subject: [PATCH 21/93] Bump version to 5.18.7 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75d6deec0..cc59e63fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.6](https://github.com/inspec/inspec/tree/v5.18.6) (2022-06-22) + +## [v5.18.7](https://github.com/inspec/inspec/tree/v5.18.7) (2022-06-23) #### Merged Pull Requests -- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) +- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) ### Changes since 5.17.4 release #### Merged Pull Requests +- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) - Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) - add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) - Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index 72ee29089..1ded889de 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.6 \ No newline at end of file +5.18.7 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 3cdd69f4d..fbeb5bb16 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.6".freeze + VERSION = "5.18.7".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 73c7ce21c..823f61be7 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.6".freeze + VERSION = "5.18.7".freeze end From e7ee813ec7d91f4a9189e14ecf915b6ab012413c Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 24 Jun 2022 14:20:47 +0530 Subject: [PATCH 22/93] Reverted fake version change and added comments for more clarity Signed-off-by: Nikita Mathur --- lib/inspec/dependencies/dependency_set.rb | 6 ++++-- lib/inspec/dsl.rb | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/inspec/dependencies/dependency_set.rb b/lib/inspec/dependencies/dependency_set.rb index e82f884b3..9c63b4e88 100644 --- a/lib/inspec/dependencies/dependency_set.rb +++ b/lib/inspec/dependencies/dependency_set.rb @@ -25,7 +25,8 @@ module Inspec def self.from_array(dependencies, cwd, cache, backend) dep_list = {} dependencies.each do |d| - key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}-0.0.0") rescue "#{d.name}-0.0.0" + # if depedent profile does not have a source version then only name is used in dependency hash + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue "#{d.name}" dep_list[key_name] = d end new(cwd, cache, dep_list, backend) @@ -40,7 +41,8 @@ module Inspec def self.flatten_dep_tree(dep_tree) dep_list = {} dep_tree.each do |d| - key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}-0.0.0") rescue "#{d.name}-0.0.0" + # if depedent profile does not have a source version then only name is used in dependency hash + key_name = (d.source_version ? "#{d.name}-#{d.source_version}" : "#{d.name}") rescue d.name dep_list[key_name] = d dep_list.merge!(flatten_dep_tree(d.dependencies)) end diff --git a/lib/inspec/dsl.rb b/lib/inspec/dsl.rb index 536d69954..da22c4dfd 100644 --- a/lib/inspec/dsl.rb +++ b/lib/inspec/dsl.rb @@ -92,6 +92,7 @@ module Inspec::DSL new_profile_id = "#{profile_id}-#{profile_version}" else dependencies.list.keys.each do |key| + # 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. profile_id_key = key.split("-") profile_id_key.pop new_profile_id = key if profile_id_key.join("-") == profile_id From 6fafd446b1e8013c83d16d9c4c08cfe130d43538 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 27 Jun 2022 21:07:30 +0530 Subject: [PATCH 23/93] Pinning minitest to 5.15.0 to fix build issue Signed-off-by: Nikita Mathur --- Gemfile | 2 +- test/helpers/mock_loader.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 58a2e8008..a1bb0b915 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ group :test do gem "json_schemer", ">= 0.2.1", "< 0.2.19" gem "m" gem "minitest-sprint", "~> 1.0" - gem "minitest", "~> 5.5" + gem "minitest", "5.15.0" gem "mocha", "~> 1.1" gem "nokogiri", "~> 1.9" gem "pry-byebug" diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index f4ae53631..2a8ae0dc6 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -227,7 +227,7 @@ class MockLoader 'sh -c \'type "/sbin/auditctl"\'' => empty.call, 'sh -c \'type "sql"\'' => cmd_exit_1.call, 'type "pwsh"' => empty.call, - 'type "netstat"' => empty.call, + 'type "/usr/sbin/netstat"' => empty.call, "sh -c 'find /etc/apache2/ports.conf -type l -maxdepth 1'" => empty.call, "sh -c 'find /etc/httpd/conf.d/*.conf -type l -maxdepth 1'" => empty.call, "sh -c 'find /etc/httpd/mods-enabled/*.conf -type l -maxdepth 1'" => empty.call, From 181ef37f2d5994d961adedddde6598449140e879 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 27 Jun 2022 15:30:17 -0400 Subject: [PATCH 24/93] Add explict /usr/sbin/netstat to mockloader, fixes routingtable tests Signed-off-by: Clinton Wolfe --- test/helpers/mock_loader.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index 2a8ae0dc6..c1c7c0386 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -409,6 +409,7 @@ class MockLoader "php -c /etc/php/7.4/cli/php.ini -r 'echo get_cfg_var(\"default_mimetype\");'" => cmd.call("get-cfg-var"), # routing_table "netstat -rn" => cmd.call("netstat-rn-linux"), + "/usr/sbin/netstat -rn" => cmd.call("netstat-rn-linux"), %{sh -c 'type "netstat"'} => empty.call, # mocks for be_immutable matcher for file resource "lsattr constantfile.txt" => cmd.call("lsattr-output"), From 007a0149427b995d5cd0bccbc6f6f1adfd491d0d Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 27 Jun 2022 15:35:46 -0400 Subject: [PATCH 25/93] Use esxi instead of undefined as an unsupported OS to silence 'unknown OS' warnings Signed-off-by: Clinton Wolfe --- test/unit/resources/cgroup_test.rb | 2 +- test/unit/resources/default_gateway_test.rb | 2 +- test/unit/resources/routing_table_test.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/resources/cgroup_test.rb b/test/unit/resources/cgroup_test.rb index e176c2669..18284b720 100644 --- a/test/unit/resources/cgroup_test.rb +++ b/test/unit/resources/cgroup_test.rb @@ -26,7 +26,7 @@ describe Inspec::Resources::Cgroup do # undefined it "check carrotking cgroup information on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("cgroup", "carrotking") + resource = MockLoader.new("exsi".to_sym).load_resource("cgroup", "carrotking") _(resource.resource_skipped?).must_equal true _(resource.resource_exception_message).must_equal "The `cgroup` resource is not supported on your OS yet." end diff --git a/test/unit/resources/default_gateway_test.rb b/test/unit/resources/default_gateway_test.rb index cf580a095..8aa57d958 100644 --- a/test/unit/resources/default_gateway_test.rb +++ b/test/unit/resources/default_gateway_test.rb @@ -19,7 +19,7 @@ describe Inspec::Resources::Defaultgateway do # unsupported os it "check ipaddress and interface of default gateway on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("default_gateway") + resource = MockLoader.new("esxi".to_sym).load_resource("default_gateway") _(resource.resource_skipped?).must_equal true _(resource.resource_failed?).must_equal true end diff --git a/test/unit/resources/routing_table_test.rb b/test/unit/resources/routing_table_test.rb index fab6fcc3c..b7b35a230 100644 --- a/test/unit/resources/routing_table_test.rb +++ b/test/unit/resources/routing_table_test.rb @@ -26,7 +26,7 @@ describe Inspec::Resources::Routingtable do # unsupported os it "check routing table information on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("routing_table") + resource = MockLoader.new("esxi".to_sym).load_resource("routing_table") _(resource.resource_skipped?).must_equal true _(resource.resource_failed?).must_equal true end From 81f89d5b314dd6a98c38a7f4a96b398e2b50a290 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 27 Jun 2022 20:15:03 +0000 Subject: [PATCH 26/93] Bump version to 5.18.8 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc59e63fa..abfdf01d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.7](https://github.com/inspec/inspec/tree/v5.18.7) (2022-06-23) + +## [v5.18.8](https://github.com/inspec/inspec/tree/v5.18.8) (2022-06-27) #### Merged Pull Requests -- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) +- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) - Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) - Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) - add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) diff --git a/VERSION b/VERSION index 1ded889de..532f2c683 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.7 \ No newline at end of file +5.18.8 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index fbeb5bb16..20aac5f37 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.7".freeze + VERSION = "5.18.8".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 823f61be7..3b36f91f8 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.7".freeze + VERSION = "5.18.8".freeze end From f3aa18b4f902ed872fa3ed4d0e0457a4a573175a Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 27 Jun 2022 21:07:30 +0530 Subject: [PATCH 27/93] Pinning minitest to 5.15.0 to fix build issue Signed-off-by: Nikita Mathur --- Gemfile | 2 +- test/helpers/mock_loader.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 58a2e8008..a1bb0b915 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ group :test do gem "json_schemer", ">= 0.2.1", "< 0.2.19" gem "m" gem "minitest-sprint", "~> 1.0" - gem "minitest", "~> 5.5" + gem "minitest", "5.15.0" gem "mocha", "~> 1.1" gem "nokogiri", "~> 1.9" gem "pry-byebug" diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index f4ae53631..2a8ae0dc6 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -227,7 +227,7 @@ class MockLoader 'sh -c \'type "/sbin/auditctl"\'' => empty.call, 'sh -c \'type "sql"\'' => cmd_exit_1.call, 'type "pwsh"' => empty.call, - 'type "netstat"' => empty.call, + 'type "/usr/sbin/netstat"' => empty.call, "sh -c 'find /etc/apache2/ports.conf -type l -maxdepth 1'" => empty.call, "sh -c 'find /etc/httpd/conf.d/*.conf -type l -maxdepth 1'" => empty.call, "sh -c 'find /etc/httpd/mods-enabled/*.conf -type l -maxdepth 1'" => empty.call, From 34cdb8780d1bf6e0aa9d068c2c3af75e85e501d9 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 27 Jun 2022 15:30:17 -0400 Subject: [PATCH 28/93] Add explict /usr/sbin/netstat to mockloader, fixes routingtable tests Signed-off-by: Clinton Wolfe --- test/helpers/mock_loader.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index 2a8ae0dc6..c1c7c0386 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -409,6 +409,7 @@ class MockLoader "php -c /etc/php/7.4/cli/php.ini -r 'echo get_cfg_var(\"default_mimetype\");'" => cmd.call("get-cfg-var"), # routing_table "netstat -rn" => cmd.call("netstat-rn-linux"), + "/usr/sbin/netstat -rn" => cmd.call("netstat-rn-linux"), %{sh -c 'type "netstat"'} => empty.call, # mocks for be_immutable matcher for file resource "lsattr constantfile.txt" => cmd.call("lsattr-output"), From 17d7822365e24fdd8d3f1b58c78446d7dff67935 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 27 Jun 2022 15:35:46 -0400 Subject: [PATCH 29/93] Use esxi instead of undefined as an unsupported OS to silence 'unknown OS' warnings Signed-off-by: Clinton Wolfe --- test/unit/resources/cgroup_test.rb | 2 +- test/unit/resources/default_gateway_test.rb | 2 +- test/unit/resources/routing_table_test.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/resources/cgroup_test.rb b/test/unit/resources/cgroup_test.rb index e176c2669..18284b720 100644 --- a/test/unit/resources/cgroup_test.rb +++ b/test/unit/resources/cgroup_test.rb @@ -26,7 +26,7 @@ describe Inspec::Resources::Cgroup do # undefined it "check carrotking cgroup information on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("cgroup", "carrotking") + resource = MockLoader.new("exsi".to_sym).load_resource("cgroup", "carrotking") _(resource.resource_skipped?).must_equal true _(resource.resource_exception_message).must_equal "The `cgroup` resource is not supported on your OS yet." end diff --git a/test/unit/resources/default_gateway_test.rb b/test/unit/resources/default_gateway_test.rb index cf580a095..8aa57d958 100644 --- a/test/unit/resources/default_gateway_test.rb +++ b/test/unit/resources/default_gateway_test.rb @@ -19,7 +19,7 @@ describe Inspec::Resources::Defaultgateway do # unsupported os it "check ipaddress and interface of default gateway on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("default_gateway") + resource = MockLoader.new("esxi".to_sym).load_resource("default_gateway") _(resource.resource_skipped?).must_equal true _(resource.resource_failed?).must_equal true end diff --git a/test/unit/resources/routing_table_test.rb b/test/unit/resources/routing_table_test.rb index fab6fcc3c..b7b35a230 100644 --- a/test/unit/resources/routing_table_test.rb +++ b/test/unit/resources/routing_table_test.rb @@ -26,7 +26,7 @@ describe Inspec::Resources::Routingtable do # unsupported os it "check routing table information on unsupported os" do - resource = MockLoader.new("undefined".to_sym).load_resource("routing_table") + resource = MockLoader.new("esxi".to_sym).load_resource("routing_table") _(resource.resource_skipped?).must_equal true _(resource.resource_failed?).must_equal true end From de9d8780f9a99cd3d53cdc67b30a03f23b43bd08 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 27 Jun 2022 23:39:30 +0000 Subject: [PATCH 30/93] Bump version to 5.18.9 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abfdf01d4..0b059d791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.8](https://github.com/inspec/inspec/tree/v5.18.8) (2022-06-27) + +## [v5.18.9](https://github.com/inspec/inspec/tree/v5.18.9) (2022-06-27) #### Merged Pull Requests -- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) +- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) ### Changes since 5.17.4 release #### Merged Pull Requests +- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) - Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) - Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) diff --git a/VERSION b/VERSION index 532f2c683..c7af3ee7d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.8 \ No newline at end of file +5.18.9 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 20aac5f37..7a5eb5494 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.8".freeze + VERSION = "5.18.9".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 3b36f91f8..c5393be48 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.8".freeze + VERSION = "5.18.9".freeze end From e39be8343fc21a5543a85a31769647613058f8d9 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Tue, 28 Jun 2022 00:31:29 -0400 Subject: [PATCH 31/93] Docs edits Signed-off-by: Clinton Wolfe --- docs-chef-io/content/inspec/cli.md | 6 +++--- docs-chef-io/content/inspec/shell.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 5aafdc895..0b9c5efff 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -138,7 +138,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). + Provides the path to the Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to execute inspec as root user). * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--self-signed`, `--no-self-signed` @@ -346,7 +346,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). + Provides the path to the Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to execute inspec as root user). * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--proxy-command=PROXY_COMMAND` @@ -564,7 +564,7 @@ This subcommand has the following additional options: * `-p`, `--port=N` Specify the login port for a remote scan. * `--podman-url` - Provides path to Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to excute inspec as root user). + Provides the path to the Podman API endpoint. Defaults to unix:///run/user/$UID/podman/podman.sock for rootless container, unix:///run/podman/podman.sock for rootful container (for this you need to execute inspec as root user). * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` diff --git a/docs-chef-io/content/inspec/shell.md b/docs-chef-io/content/inspec/shell.md index 3212e9251..45108f447 100644 --- a/docs-chef-io/content/inspec/shell.md +++ b/docs-chef-io/content/inspec/shell.md @@ -36,7 +36,7 @@ inspec help shell # This will describe inspec shell usage If you wish to connect to a remote machine (called a target within InSpec), you can use the `-t` flag. We support connecting using SSH, -WinRM ,docker and podman. If no target is provided, we implicitly support the +WinRM, Docker, Podman and many other target types. If no target is provided, we implicitly support the "local" target - i.e. tests running on the current machine running InSpec. For an SSH connection, use `-i` for specifying SSH key files, and the `--sudo*` commands for requesting a privilege escalation after From e6c66d0398b18204d1d4a4854e80d50096ce788a Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Tue, 28 Jun 2022 00:33:58 -0400 Subject: [PATCH 32/93] Add dependencies on train 3.10.0 for podman support Signed-off-by: Clinton Wolfe --- inspec-core.gemspec | 2 +- inspec.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/inspec-core.gemspec b/inspec-core.gemspec index 67032f4ea..92948513f 100644 --- a/inspec-core.gemspec +++ b/inspec-core.gemspec @@ -45,5 +45,5 @@ Gem::Specification.new do |spec| spec.add_dependency "semverse", "~> 3.0" spec.add_dependency "multipart-post", "~> 2.0" - spec.add_dependency "train-core", "~> 3.0" + spec.add_dependency "train-core", "~> 3.10" end diff --git a/inspec.gemspec b/inspec.gemspec index 995f4e66e..890295ca4 100644 --- a/inspec.gemspec +++ b/inspec.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |spec| spec.add_dependency "inspec-core", "= #{Inspec::VERSION}" - spec.add_dependency "train", "~> 3.0" + spec.add_dependency "train", "~> 3.10" # cookstyle support for inspec check # Added here not because they are compiled, but to keep chef-client lightweight From 21bc15771f71e2426be128ae0f99b68f3b1acf94 Mon Sep 17 00:00:00 2001 From: Ian Maddaus Date: Tue, 28 Jun 2022 11:10:36 -0400 Subject: [PATCH 33/93] Add k8s section to resources index page Signed-off-by: Ian Maddaus --- docs-chef-io/content/inspec/resources/_index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs-chef-io/content/inspec/resources/_index.md b/docs-chef-io/content/inspec/resources/_index.md index 887bc1cc4..8e4ca031e 100644 --- a/docs-chef-io/content/inspec/resources/_index.md +++ b/docs-chef-io/content/inspec/resources/_index.md @@ -56,3 +56,7 @@ The following resources work on Windows operating systems. ## Habitat {{< inspec/inspec_resources platform="habitat" >}} + +## Kubernetes + +{{< inspec/inspec_resources platform="k8s" >}} From 8bcfc38fe02ffff4bc0a6709e49516e2a471357b Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 29 Jun 2022 17:05:57 +0530 Subject: [PATCH 34/93] Windows fix for dependent profiles Signed-off-by: Nikita Mathur --- lib/inspec/profile.rb | 16 +++++++++------- test/functional/inspec_exec_test.rb | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/inspec/profile.rb b/lib/inspec/profile.rb index 126264dc3..a435a7187 100644 --- a/lib/inspec/profile.rb +++ b/lib/inspec/profile.rb @@ -350,22 +350,24 @@ module Inspec def load_libraries return @runner_context if @libraries_loaded - locked_dependencies.dep_list.each_with_index do |(_name, dep), i| + locked_dependencies.dep_list.each_with_index do |(_name, dep), x| d = dep.profile # this will force a dependent profile load so we are only going to add # this metadata if the parent profile is supported. if @supports_platform && !d.supports_platform? # since ruby 1.9 hashes are ordered so we can just use index values here # TODO: NO! this is a violation of encapsulation to an extreme - metadata.dependencies[i][:status] = "skipped" - msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'." - metadata.dependencies[i][:status_message] = msg - metadata.dependencies[i][:skip_message] = msg # Repeat as skip_message for backward compatibility + if metadata.dependencies[x] + metadata.dependencies[x][:status] = "skipped" + msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'." + metadata.dependencies[x][:status_message] = msg + metadata.dependencies[x][:skip_message] = msg # Repeat as skip_message for backward compatibility + end next - elsif metadata.dependencies[i] + elsif metadata.dependencies[x] # Currently wrapper profiles will load all dependencies, and then we # load them again when we dive down. This needs to be re-done. - metadata.dependencies[i][:status] = "loaded" + metadata.dependencies[x][:status] = "loaded" end # rubocop:disable Layout/ExtraSpacing diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index b8779edc5..ddbe8bf8e 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1306,7 +1306,8 @@ EOT end end - describe "when profiles are dependent on different versions of same profile" do + describe "when profiles are dependent on different versions of same profile - test in unix" do + skip_windows! let(:profile) { "#{profile_path}/git-fetcher/inheritance/parent-profile" } let(:run_result) { run_inspec_process("exec #{profile}") } it "should evaluate all test controls of all versions correctly" do @@ -1317,4 +1318,16 @@ EOT _(run_result.stdout).must_include "sshd-50" end end + + describe "when profiles are dependent on different versions of same profile - test in windows" do + if is_windows? + let(:profile) { "#{profile_path}/git-fetcher/inheritance-windows/parent-profile" } + let(:run_result) { run_inspec_process("exec #{profile}") } + it "should evaluate all test controls of all versions correctly" do + _(run_result.stderr).must_be_empty + _(run_result.stdout).must_include "2.1.8" + _(run_result.stdout).must_include "2.1.6" + end + end + end end From 5617ed819c7f1d8aadb88cb7e819ec3027a0b099 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 29 Jun 2022 17:10:54 +0530 Subject: [PATCH 35/93] Added inheritance test for windows Signed-off-by: Nikita Mathur --- lib/inspec/profile.rb | 14 +++++++------- .../child-profile-1/controls/example.rb | 0 .../child-profile-1/inspec.yml | 15 +++++++++++++++ .../child-profile-2/controls/example.rb | 0 .../child-profile-2/inspec.yml | 14 ++++++++++++++ .../parent-profile/controls/example.rb | 2 ++ .../inheritance-windows/parent-profile/inspec.yml | 15 +++++++++++++++ 7 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/controls/example.rb create mode 100644 test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/inspec.yml diff --git a/lib/inspec/profile.rb b/lib/inspec/profile.rb index a435a7187..b6fd28333 100644 --- a/lib/inspec/profile.rb +++ b/lib/inspec/profile.rb @@ -350,24 +350,24 @@ module Inspec def load_libraries return @runner_context if @libraries_loaded - locked_dependencies.dep_list.each_with_index do |(_name, dep), x| + locked_dependencies.dep_list.each_with_index do |(_name, dep), index| d = dep.profile # this will force a dependent profile load so we are only going to add # this metadata if the parent profile is supported. if @supports_platform && !d.supports_platform? # since ruby 1.9 hashes are ordered so we can just use index values here # TODO: NO! this is a violation of encapsulation to an extreme - if metadata.dependencies[x] - metadata.dependencies[x][:status] = "skipped" + if metadata.dependencies[index] + metadata.dependencies[index][:status] = "skipped" msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'." - metadata.dependencies[x][:status_message] = msg - metadata.dependencies[x][:skip_message] = msg # Repeat as skip_message for backward compatibility + metadata.dependencies[index][:status_message] = msg + metadata.dependencies[index][:skip_message] = msg # Repeat as skip_message for backward compatibility end next - elsif metadata.dependencies[x] + elsif metadata.dependencies[index] # Currently wrapper profiles will load all dependencies, and then we # load them again when we dive down. This needs to be re-done. - metadata.dependencies[x][:status] = "loaded" + metadata.dependencies[index][:status] = "loaded" end # rubocop:disable Layout/ExtraSpacing diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/controls/example.rb new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml new file mode 100644 index 000000000..1ba74285f --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml @@ -0,0 +1,15 @@ +name: child-profile-1 +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: ssh + git: https://github.com/dev-sec/windows-baseline.git + tag: 2.1.8 + \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/controls/example.rb new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml new file mode 100644 index 000000000..b711a0070 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml @@ -0,0 +1,14 @@ +name: child-profile-2 +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: ssh + git: https://github.com/dev-sec/windows-baseline.git + tag: 2.1.6 \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/controls/example.rb b/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/controls/example.rb new file mode 100644 index 000000000..71ed7c9b3 --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/controls/example.rb @@ -0,0 +1,2 @@ +include_controls "child-profile-1" +include_controls "child-profile-2" \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/inspec.yml new file mode 100644 index 000000000..da868a46f --- /dev/null +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/parent-profile/inspec.yml @@ -0,0 +1,15 @@ +name: parent-profile +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os +depends: + - name: child-profile-2 + path: ../child-profile-2 + - name: child-profile-1 + path: ../child-profile-1 \ No newline at end of file From 26a40c1bbb4441fc899ba48bac5385127deacce4 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 30 Jun 2022 13:37:12 +0530 Subject: [PATCH 36/93] Fix for if conditions placement in exec test file Signed-off-by: Nikita Mathur --- test/functional/inspec_exec_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index ddbe8bf8e..65a1dbabf 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1307,10 +1307,10 @@ EOT end describe "when profiles are dependent on different versions of same profile - test in unix" do - skip_windows! let(:profile) { "#{profile_path}/git-fetcher/inheritance/parent-profile" } let(:run_result) { run_inspec_process("exec #{profile}") } it "should evaluate all test controls of all versions correctly" do + skip_windows! _(run_result.stderr).must_be_empty _(run_result.stdout).must_include "2.7.0" _(run_result.stdout).must_include "2.6.0" @@ -1320,10 +1320,10 @@ EOT end describe "when profiles are dependent on different versions of same profile - test in windows" do - if is_windows? - let(:profile) { "#{profile_path}/git-fetcher/inheritance-windows/parent-profile" } - let(:run_result) { run_inspec_process("exec #{profile}") } - it "should evaluate all test controls of all versions correctly" do + let(:profile) { "#{profile_path}/git-fetcher/inheritance-windows/parent-profile" } + let(:run_result) { run_inspec_process("exec #{profile}") } + it "should evaluate all test controls of all versions correctly" do + if is_windows? _(run_result.stderr).must_be_empty _(run_result.stdout).must_include "2.1.8" _(run_result.stdout).must_include "2.1.6" From df0397fa61f830145f3feab0bdb4cb6ed702ed4d Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 30 Jun 2022 17:51:26 +0530 Subject: [PATCH 37/93] Windows testing with different versions of dependent profiles Signed-off-by: Nikita Mathur --- .../inheritance-windows/child-profile-1/inspec.yml | 2 +- .../inheritance-windows/child-profile-2/inspec.yml | 2 +- test/functional/inspec_exec_test.rb | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml index 1ba74285f..82ea2930e 100644 --- a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-1/inspec.yml @@ -11,5 +11,5 @@ supports: depends: - name: ssh git: https://github.com/dev-sec/windows-baseline.git - tag: 2.1.8 + tag: 1.1.2 \ No newline at end of file diff --git a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml index b711a0070..25cc3afde 100644 --- a/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml +++ b/test/fixtures/profiles/git-fetcher/inheritance-windows/child-profile-2/inspec.yml @@ -11,4 +11,4 @@ supports: depends: - name: ssh git: https://github.com/dev-sec/windows-baseline.git - tag: 2.1.6 \ No newline at end of file + tag: 1.1.0 \ No newline at end of file diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 65a1dbabf..4bff77890 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1324,9 +1324,8 @@ EOT let(:run_result) { run_inspec_process("exec #{profile}") } it "should evaluate all test controls of all versions correctly" do if is_windows? - _(run_result.stderr).must_be_empty - _(run_result.stdout).must_include "2.1.8" - _(run_result.stdout).must_include "2.1.6" + _(run_result.stdout).must_include "1.1.2" + _(run_result.stdout).must_include "1.1.0" end end end From ec43054411d2e067a6b53ec5cf5600eaa3953d3c Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 1 Jul 2022 14:13:58 +0530 Subject: [PATCH 38/93] Platform checks on tests for unix and windows Signed-off-by: Nikita Mathur --- test/functional/inspec_exec_test.rb | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 4bff77890..9c4be8756 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1306,24 +1306,26 @@ EOT end end - describe "when profiles are dependent on different versions of same profile - test in unix" do - let(:profile) { "#{profile_path}/git-fetcher/inheritance/parent-profile" } - let(:run_result) { run_inspec_process("exec #{profile}") } - it "should evaluate all test controls of all versions correctly" do - skip_windows! - _(run_result.stderr).must_be_empty - _(run_result.stdout).must_include "2.7.0" - _(run_result.stdout).must_include "2.6.0" - _(run_result.stdout).must_include "sshd-01" - _(run_result.stdout).must_include "sshd-50" + unless windows? + describe "when profiles are dependent on different versions of same profile - test in unix" do + let(:profile) { "#{profile_path}/git-fetcher/inheritance/parent-profile" } + let(:run_result) { run_inspec_process("exec #{profile}") } + it "should evaluate all test controls of all versions correctly" do + skip_windows! + _(run_result.stderr).must_be_empty + _(run_result.stdout).must_include "2.7.0" + _(run_result.stdout).must_include "2.6.0" + _(run_result.stdout).must_include "sshd-01" + _(run_result.stdout).must_include "sshd-50" + end end end - describe "when profiles are dependent on different versions of same profile - test in windows" do - let(:profile) { "#{profile_path}/git-fetcher/inheritance-windows/parent-profile" } - let(:run_result) { run_inspec_process("exec #{profile}") } - it "should evaluate all test controls of all versions correctly" do - if is_windows? + if windows? + describe "when profiles are dependent on different versions of same profile - test in windows" do + let(:profile) { "#{profile_path}/git-fetcher/inheritance-windows/parent-profile" } + let(:run_result) { run_inspec_process("exec #{profile}") } + it "should evaluate all test controls of all versions correctly" do _(run_result.stdout).must_include "1.1.2" _(run_result.stdout).must_include "1.1.0" end From 63835f42bc39ad711d8a8ba2797d480edc0b909d Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Fri, 1 Jul 2022 12:54:30 +0000 Subject: [PATCH 39/93] Bump version to 5.18.10 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b059d791..8acea4ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.9](https://github.com/inspec/inspec/tree/v5.18.9) (2022-06-27) + +## [v5.18.10](https://github.com/inspec/inspec/tree/v5.18.10) (2022-07-01) #### Merged Pull Requests -- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) +- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) - CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) - Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) diff --git a/VERSION b/VERSION index c7af3ee7d..db4725fd4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.9 \ No newline at end of file +5.18.10 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 7a5eb5494..d5669341a 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.9".freeze + VERSION = "5.18.10".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index c5393be48..d79557ba2 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.9".freeze + VERSION = "5.18.10".freeze end From 136f01c0f55ce549a84fc772c6b5b6601129bd84 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Tue, 5 Jul 2022 09:43:53 +0000 Subject: [PATCH 40/93] Bump version to 5.18.11 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8acea4ddb..392f33134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.10](https://github.com/inspec/inspec/tree/v5.18.10) (2022-07-01) + +## [v5.18.11](https://github.com/inspec/inspec/tree/v5.18.11) (2022-07-05) #### Merged Pull Requests -- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) +- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) - Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) - CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) diff --git a/VERSION b/VERSION index db4725fd4..170881d53 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.10 \ No newline at end of file +5.18.11 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index d5669341a..dae0993ae 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.10".freeze + VERSION = "5.18.11".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index d79557ba2..06176a058 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.10".freeze + VERSION = "5.18.11".freeze end From 355b7160d3c38e11435c32b196ecf127104d9c4b Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Wed, 6 Jul 2022 09:37:13 -0400 Subject: [PATCH 41/93] Add draft of attestations design doc Signed-off-by: Clinton Wolfe --- dev-docs/attestations.md | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 dev-docs/attestations.md diff --git a/dev-docs/attestations.md b/dev-docs/attestations.md new file mode 100644 index 000000000..c8bdd8404 --- /dev/null +++ b/dev-docs/attestations.md @@ -0,0 +1,80 @@ +# Attestations + +## Use Cases + +As a compliance officer +I want to be able to mark skipped controls as manually passed or failed +so that I can manually complete the profile + +As a compliance officer +I want to set an expiration date and a justification on my attestations +so that I can control their application + +As a compliance officer +I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON) +so that I can use a familiar file format + +When used in conjunction with Enhanced Outcomes, this becomes handling Not Reviewed controls. + +## Mechanism + +### CLI option desirable + +`inspec exec profilename --attestation-file file.???` + +Option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments to the option. + +File may be either YAML, XLSX, CSV, or JSON. + +#### YAML and JSON + +Array of Hashes. + +#### XLSX and CSV + +XLSX is the first sheet in the file. + +Both formats assume a header row. + +### Fields in the file + +#### control_id + +Required. Matches control ID of the control. + +#### justification + +Required. Free text field, used as explanation for the control when displayed. + +#### evidence_url + +Optional. URL to some sort of evidence, determined by the user, that supports the justification. + +#### expiration_date + +Optional. If present, the attestation expires at the end of the date given. + +#### status + +Optional, default "passed". Either passed or failed. If the attestation should indicate that the control is a failure, set this to "failed". + +### Implementation + +When running, at the RunData stage, attestations are handled by the following process: + +1. Locate matching controls my matching the control ID. + +2. Inject an artificial test result into the control. Use the attestation justification as the result message. + +3. If the attestation is expired, set the new test result to Skip. + +4. If the attestation is not expired, set the new test result to the status given on the attestation data (default pass). + +5. Record a copy of the attestation data structure in the Control RunData structure. + +### Compatibility + +To support backwards compatibility with existing MITRE work, support will be added (but not otherwise documented) for the following fields: + + * explanation - equivalent of justification + * updated (Date) and frequency (string enum) - together, equivalent of expiration date. From fe82c188a9db8c341ac2e78d1014cbf24e3f4773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Jul 2022 04:32:36 +0000 Subject: [PATCH 42/93] Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus Bumps [omnibus-software](https://github.com/chef/omnibus-software) from `a9b13a0` to `7bb8c7b`. - [Release notes](https://github.com/chef/omnibus-software/releases) - [Commits](https://github.com/chef/omnibus-software/compare/a9b13a09c2f89e4618225dd41b2fd08f2150a1ba...7bb8c7b4fd2cc5829fba9d52df1dac683fd88069) --- updated-dependencies: - dependency-name: omnibus-software dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- omnibus/Gemfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 5c4688dde..70253e3fa 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: a9b13a09c2f89e4618225dd41b2fd08f2150a1ba + revision: 7bb8c7b4fd2cc5829fba9d52df1dac683fd88069 branch: main specs: omnibus-software (4.0.0) @@ -34,8 +34,8 @@ GEM artifactory (3.0.15) awesome_print (1.9.2) aws-eventstream (1.2.0) - aws-partitions (1.595.0) - aws-sdk-core (3.131.1) + aws-partitions (1.603.0) + aws-sdk-core (3.131.2) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) @@ -248,7 +248,7 @@ GEM tomlrb (>= 1.2, < 3.0) tty-box (~> 0.6) tty-prompt (~> 0.20) - license_scout (1.3.1) + license_scout (1.3.2) ffi-yajl (~> 2.2) mixlib-shellout (>= 2.2, < 4.0) toml-rb (>= 1, < 3) @@ -264,7 +264,7 @@ GEM mixlib-log mixlib-authentication (3.0.10) mixlib-cli (2.1.8) - mixlib-config (3.0.9) + mixlib-config (3.0.27) tomlrb mixlib-install (3.12.16) mixlib-shellout @@ -278,7 +278,7 @@ GEM ffi-win32-extensions (~> 1.0.3) win32-process (~> 0.9) wmi-lite (~> 1.0) - mixlib-shellout (3.2.7-x64-unknown) + mixlib-shellout (3.2.7-x64-mingw-ucrt) chef-utils ffi-win32-extensions (~> 1.0.3) win32-process (~> 0.9) @@ -384,7 +384,7 @@ GEM toml-rb (2.1.2) citrus (~> 3.0, > 3.0) tomlrb (1.3.0) - train-core (3.9.2) + train-core (3.10.1) addressable (~> 2.5) ffi (!= 1.13.0) json (>= 1.8, < 3.0) From 6ab3b4398ba851a0d31d52308b7cc107c2da7e9a Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Thu, 7 Jul 2022 17:18:08 +0530 Subject: [PATCH 43/93] Doc Review Signed-off-by: Deepa Kumaraswamy --- dev-docs/attestations.md | 43 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/dev-docs/attestations.md b/dev-docs/attestations.md index c8bdd8404..f7a4d2de2 100644 --- a/dev-docs/attestations.md +++ b/dev-docs/attestations.md @@ -2,19 +2,14 @@ ## Use Cases -As a compliance officer -I want to be able to mark skipped controls as manually passed or failed -so that I can manually complete the profile +As a compliance officer, I want to mark skipped controls as manually passed or failed so I can manually complete the profile. -As a compliance officer -I want to set an expiration date and a justification on my attestations -so that I can control their application +As a compliance officer, I want to set an expiration date and a justification for my attestations so that I can control their application. -As a compliance officer -I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON) -so that I can use a familiar file format +As a compliance officer, I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON), +so that I can use a familiar file format. -When used in conjunction with Enhanced Outcomes, this becomes handling Not Reviewed controls. +When used with Enhanced Outcomes, this becomes handling `Not Reviewed` controls. ## Mechanism @@ -22,13 +17,13 @@ When used in conjunction with Enhanced Outcomes, this becomes handling Not Revie `inspec exec profilename --attestation-file file.???` -Option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments to the option. +An option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments for the option. -File may be either YAML, XLSX, CSV, or JSON. +The file can be either of the following formats: `YAML`, `XLSX`, `CSV`, or `JSON`. #### YAML and JSON -Array of Hashes. +An array of Hashes. #### XLSX and CSV @@ -40,29 +35,31 @@ Both formats assume a header row. #### control_id -Required. Matches control ID of the control. +_Required_. Matches control ID of the control. #### justification -Required. Free text field, used as explanation for the control when displayed. +_Required_. Free text field, used as an explanation for the control when displayed. #### evidence_url -Optional. URL to some sort of evidence, determined by the user, that supports the justification. +_Optional_. URL to some evidence, determined by the user, supports the justification. #### expiration_date -Optional. If present, the attestation expires at the end of the date given. +_Optional_. If present, the attestation expires at the end of the date given. #### status -Optional, default "passed". Either passed or failed. If the attestation should indicate that the control is a failure, set this to "failed". +_Optional_. + +Default `passed`. If the attestation should indicate that the control is a failure, set this to `failed`. ### Implementation -When running, at the RunData stage, attestations are handled by the following process: +When running, at the **RunData** stage, attestations are handled by the following process: -1. Locate matching controls my matching the control ID. +1. Locate matching controls by matching the control ID. 2. Inject an artificial test result into the control. Use the attestation justification as the result message. @@ -74,7 +71,7 @@ When running, at the RunData stage, attestations are handled by the following pr ### Compatibility -To support backwards compatibility with existing MITRE work, support will be added (but not otherwise documented) for the following fields: +To support backward compatibility with existing MITRE work, support will be added (but not otherwise documented) for the following fields: - * explanation - equivalent of justification - * updated (Date) and frequency (string enum) - together, equivalent of expiration date. +* explanation - the equivalent of justification +* updated (Date) and frequency (string enum) - together, the equivalent of the expiration date. From f30bc794490088bcdd5b54c765ef83cb702ba32a Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 7 Jul 2022 11:59:31 -0400 Subject: [PATCH 44/93] minor corrections Signed-off-by: Clinton Wolfe --- dev-docs/attestations.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/dev-docs/attestations.md b/dev-docs/attestations.md index f7a4d2de2..214b48056 100644 --- a/dev-docs/attestations.md +++ b/dev-docs/attestations.md @@ -2,11 +2,16 @@ ## Use Cases -As a compliance officer, I want to mark skipped controls as manually passed or failed so I can manually complete the profile. +As a compliance officer, +I want to mark skipped controls as manually passed or failed +so that I can manually complete the profile. -As a compliance officer, I want to set an expiration date and a justification for my attestations so that I can control their application. +As a compliance officer, +I want to set an expiration date and a justification for my attestations +so that I can control their application. -As a compliance officer, I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON), +As a compliance officer, +I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON), so that I can use a familiar file format. When used with Enhanced Outcomes, this becomes handling `Not Reviewed` controls. @@ -17,9 +22,9 @@ When used with Enhanced Outcomes, this becomes handling `Not Reviewed` controls. `inspec exec profilename --attestation-file file.???` -An option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments for the option. +The new option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments for the option. -The file can be either of the following formats: `YAML`, `XLSX`, `CSV`, or `JSON`. +The file can be any of the following formats: `YAML`, `XLSX`, `CSV`, or `JSON`. #### YAML and JSON @@ -51,7 +56,7 @@ _Optional_. If present, the attestation expires at the end of the date given. #### status -_Optional_. +_Optional_. Default `passed`. If the attestation should indicate that the control is a failure, set this to `failed`. From 8c72cf239d5888b31f6e0bcbb6dae7772a0cd0c0 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Sat, 9 Jul 2022 08:41:59 +0000 Subject: [PATCH 45/93] Bump version to 5.18.12 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392f33134..7ad511781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.11](https://github.com/inspec/inspec/tree/v5.18.11) (2022-07-05) + +## [v5.18.12](https://github.com/inspec/inspec/tree/v5.18.12) (2022-07-09) #### Merged Pull Requests -- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) +- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) ### Changes since 5.17.4 release #### Merged Pull Requests +- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) - Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) - Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) - CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index 170881d53..16df39dac 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.11 \ No newline at end of file +5.18.12 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index dae0993ae..7ee18bd4c 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.11".freeze + VERSION = "5.18.12".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 06176a058..b6b1dd67c 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.11".freeze + VERSION = "5.18.12".freeze end From 8677421601eecee4c4c7ecfbcc692cf8ff69be6e Mon Sep 17 00:00:00 2001 From: Vasu1105 Date: Sat, 9 Jul 2022 13:31:55 +0530 Subject: [PATCH 46/93] This might be the reson of inspec build failure Signed-off-by: Vasu1105 --- omnibus/Gemfile.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 70253e3fa..39b3c4a9b 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -47,7 +47,7 @@ GEM aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) - aws-sdk-secretsmanager (1.62.0) + aws-sdk-secretsmanager (1.64.0) aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) aws-sigv4 (1.5.0) @@ -194,8 +194,8 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) ffi (1.15.5) + ffi (1.15.5-x64-mingw-ucrt) ffi (1.15.5-x64-mingw32) - ffi (1.15.5-x64-unknown) ffi (1.15.5-x86-mingw32) ffi-libarchive (1.1.3) ffi (~> 1.0) @@ -240,7 +240,7 @@ GEM iso8601 (0.13.0) jmespath (1.6.1) json (2.6.2) - kitchen-vagrant (1.11.0) + kitchen-vagrant (1.12.0) test-kitchen (>= 1.4, < 4) libyajl2 (2.1.0) license-acceptance (2.1.13) @@ -266,7 +266,7 @@ GEM mixlib-cli (2.1.8) mixlib-config (3.0.27) tomlrb - mixlib-install (3.12.16) + mixlib-install (3.12.19) mixlib-shellout mixlib-versioning thor @@ -295,7 +295,7 @@ GEM net-ssh-gateway (2.0.0) net-ssh (>= 4.0.0) nori (2.6.0) - octokit (4.25.0) + octokit (4.25.1) faraday (>= 1, < 3) sawyer (~> 0.9) ohai (17.9.0) @@ -327,7 +327,7 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (4.0.7) - rack (2.2.3.1) + rack (2.2.4) rainbow (3.1.1) retryable (3.0.5) rexml (3.2.5) @@ -366,7 +366,7 @@ GEM strings-ansi (0.2.0) structured_warnings (0.4.0) syslog-logger (1.6.8) - test-kitchen (3.2.2) + test-kitchen (3.3.1) bcrypt_pbkdf (~> 1.0) chef-utils (>= 16.4.35) ed25519 (~> 1.2) @@ -413,7 +413,7 @@ GEM pastel (~> 0.8) strings (~> 0.2.0) tty-screen (~> 0.8) - unicode-display_width (2.1.0) + unicode-display_width (2.2.0) unicode_utils (1.4.0) uuidtools (2.2.0) vault (0.17.0) @@ -465,8 +465,8 @@ GEM PLATFORMS ruby + x64-mingw x64-mingw32 - x64-unknown x86-mingw32 DEPENDENCIES From ddc7cfb56cd689dd145ea3d52b502ecc245bb040 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Sat, 9 Jul 2022 16:50:46 +0000 Subject: [PATCH 47/93] Bump version to 5.18.13 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad511781..9d044d9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.12](https://github.com/inspec/inspec/tree/v5.18.12) (2022-07-09) + +## [v5.18.13](https://github.com/inspec/inspec/tree/v5.18.13) (2022-07-09) #### Merged Pull Requests -- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) +- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) ### Changes since 5.17.4 release #### Merged Pull Requests +- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) - Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) - Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) - Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) diff --git a/VERSION b/VERSION index 16df39dac..5131e4495 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.12 \ No newline at end of file +5.18.13 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 7ee18bd4c..f6168376a 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.12".freeze + VERSION = "5.18.13".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index b6b1dd67c..3a8ca662c 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.12".freeze + VERSION = "5.18.13".freeze end From de7931d0c2a96cf2c837b2055ed02e3359481b3c Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 11 Jul 2022 13:52:19 +0000 Subject: [PATCH 48/93] Bump version to 5.18.14 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d044d9e1..aad46e46f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.13](https://github.com/inspec/inspec/tree/v5.18.13) (2022-07-09) + +## [v5.18.14](https://github.com/inspec/inspec/tree/v5.18.14) (2022-07-11) #### Merged Pull Requests -- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe)) ### Changes since 5.17.4 release #### Merged Pull Requests +- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) - Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) - Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) diff --git a/VERSION b/VERSION index 5131e4495..00c93947c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.13 \ No newline at end of file +5.18.14 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index f6168376a..3de509642 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.13".freeze + VERSION = "5.18.14".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 3a8ca662c..3fbd7d11a 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.13".freeze + VERSION = "5.18.14".freeze end From 6a238d7818bc278b975d10bbe8b8f3961097c302 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Wed, 13 Jul 2022 17:12:44 +0000 Subject: [PATCH 49/93] Executed '.expeditor/update_dockerfile.sh' Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 75 +++++++++++++++++++++++++--------------------------- Dockerfile | 2 +- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aad46e46f..8eb9a6bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,48 +1,46 @@ # Change Log - -## [v5.18.14](https://github.com/inspec/inspec/tree/v5.18.14) (2022-07-11) - -#### Merged Pull Requests -- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe)) + - -### Changes since 5.17.4 release - -#### Merged Pull Requests -- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe)) -- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) -- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) -- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) -- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) -- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) -- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) -- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) -- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) -- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) -- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) -- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) -- Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-291: Fix `processes` resource to consider processes without `path` on Windows [#6100](https://github.com/inspec/inspec/pull/6100) ([ahasunos](https://github.com/ahasunos)) -- CFINSPEC-167: Profile Signing Rollup [#5995](https://github.com/inspec/inspec/pull/5995) ([Vasu1105](https://github.com/Vasu1105)) -- Bump berkshelf from 8.0.0 to 8.0.2 in /omnibus [#6114](https://github.com/inspec/inspec/pull/6114) ([dependabot[bot]](https://github.com/dependabot[bot])) -- CFINSPEC-262 - Handle resource_id in error situation [#6119](https://github.com/inspec/inspec/pull/6119) ([Vasu1105](https://github.com/Vasu1105)) -- Handle resource_id in error situations [#6118](https://github.com/inspec/inspec/pull/6118) ([ahasunos](https://github.com/ahasunos)) -- CFINSPEC-273 Adds resource_id group 12 [#6112](https://github.com/inspec/inspec/pull/6112) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-270 Adds resource_id group9 [#6111](https://github.com/inspec/inspec/pull/6111) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-265 Group 4 - Added resource_id in resources [#6109](https://github.com/inspec/inspec/pull/6109) ([Nik08](https://github.com/Nik08)) -- CFINSPEC-269 Adds resource_id group 8 [#6107](https://github.com/inspec/inspec/pull/6107) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-268 Adds resource_id group 7 [#6105](https://github.com/inspec/inspec/pull/6105) ([Vasu1105](https://github.com/Vasu1105)) -- Fix the key duplication error warning in the mock_loader.rb [#6120](https://github.com/inspec/inspec/pull/6120) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-266: resource_ids group 5 [#6103](https://github.com/inspec/inspec/pull/6103) ([ahasunos](https://github.com/ahasunos)) -- CFINSPEC-262 Adds resource_id group 1 [#6102](https://github.com/inspec/inspec/pull/6102) ([Vasu1105](https://github.com/Vasu1105)) -- CFINSPEC-267: resource_ids group 6 [#6101](https://github.com/inspec/inspec/pull/6101) ([ahasunos](https://github.com/ahasunos)) -- CFINSPEC-95: Enhance `x509_certificate` resource [#6041](https://github.com/inspec/inspec/pull/6041) ([ahasunos](https://github.com/ahasunos)) -- Bump rack from 2.2.3 to 2.2.3.1 in /omnibus [#6098](https://github.com/inspec/inspec/pull/6098) ([dependabot[bot]](https://github.com/dependabot[bot])) + +## [v5.18.14](https://github.com/inspec/inspec/tree/v5.18.14) (2022-07-13) + +#### Merged Pull Requests +- Bump rack from 2.2.3 to 2.2.3.1 in /omnibus [#6098](https://github.com/inspec/inspec/pull/6098) ([dependabot[bot]](https://github.com/dependabot[bot])) +- CFINSPEC-95: Enhance `x509_certificate` resource [#6041](https://github.com/inspec/inspec/pull/6041) ([ahasunos](https://github.com/ahasunos)) +- CFINSPEC-267: resource_ids group 6 [#6101](https://github.com/inspec/inspec/pull/6101) ([ahasunos](https://github.com/ahasunos)) +- CFINSPEC-262 Adds resource_id group 1 [#6102](https://github.com/inspec/inspec/pull/6102) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-266: resource_ids group 5 [#6103](https://github.com/inspec/inspec/pull/6103) ([ahasunos](https://github.com/ahasunos)) +- Fix the key duplication error warning in the mock_loader.rb [#6120](https://github.com/inspec/inspec/pull/6120) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-268 Adds resource_id group 7 [#6105](https://github.com/inspec/inspec/pull/6105) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-269 Adds resource_id group 8 [#6107](https://github.com/inspec/inspec/pull/6107) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-265 Group 4 - Added resource_id in resources [#6109](https://github.com/inspec/inspec/pull/6109) ([Nik08](https://github.com/Nik08)) +- CFINSPEC-270 Adds resource_id group9 [#6111](https://github.com/inspec/inspec/pull/6111) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-273 Adds resource_id group 12 [#6112](https://github.com/inspec/inspec/pull/6112) ([Vasu1105](https://github.com/Vasu1105)) +- Handle resource_id in error situations [#6118](https://github.com/inspec/inspec/pull/6118) ([ahasunos](https://github.com/ahasunos)) +- CFINSPEC-262 - Handle resource_id in error situation [#6119](https://github.com/inspec/inspec/pull/6119) ([Vasu1105](https://github.com/Vasu1105)) +- Bump berkshelf from 8.0.0 to 8.0.2 in /omnibus [#6114](https://github.com/inspec/inspec/pull/6114) ([dependabot[bot]](https://github.com/dependabot[bot])) +- CFINSPEC-167: Profile Signing Rollup [#5995](https://github.com/inspec/inspec/pull/5995) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-291: Fix `processes` resource to consider processes without `path` on Windows [#6100](https://github.com/inspec/inspec/pull/6100) ([ahasunos](https://github.com/ahasunos)) +- Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105)) +- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) +- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe)) +- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158)) +- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08)) +- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot])) +- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08)) +- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe)) +- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd)) +- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08)) +- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot])) +- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe)) + + ## [v5.17.4](https://github.com/inspec/inspec/tree/v5.17.4) (2022-05-25) #### Merged Pull Requests @@ -63,7 +61,6 @@ - fixing bad markdown syntax [#6066](https://github.com/inspec/inspec/pull/6066) ([replicajune](https://github.com/replicajune)) - Add vale config to docs in inspec repository [#6065](https://github.com/inspec/inspec/pull/6065) ([IanMadd](https://github.com/IanMadd)) - Remove Hugo version from Netlify config [#6075](https://github.com/inspec/inspec/pull/6075) ([IanMadd](https://github.com/IanMadd)) - ## [v5.14.0](https://github.com/inspec/inspec/tree/v5.14.0) (2022-04-21) diff --git a/Dockerfile b/Dockerfile index afef388bf..9d8a3a9ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:18.04 LABEL maintainer="Chef Software, Inc. " -ARG VERSION=5.17.4 +ARG VERSION=5.18.14 ARG CHANNEL=stable ENV PATH=/opt/inspec/bin:/opt/inspec/embedded/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin From c3266e801028901f1990f976c273bc2a12dc7060 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Wed, 13 Jul 2022 16:35:19 -0400 Subject: [PATCH 50/93] Clarify license requirement for currently supported versions Signed-off-by: Clinton Wolfe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4863774c9..05d462682 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ inspec exec test.rb -t docker://container_id Chef InSpec requires Ruby ( >= 2.7 ). -Note: Versions of Chef InSpec 4.0 and later require accepting the EULA to use. Please visit the [license acceptance page](https://docs.chef.io/chef_license_accept.html) on the Chef docs site for more information. +All currently supported versions of Chef InSpec (4.0 and later) require accepting the EULA to use. Please visit the [license acceptance page](https://docs.chef.io/chef_license_accept.html) on the Chef docs site for more information. ### Install as package From 833bfd876644bd062f71900bcad18f18dc656faf Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Wed, 13 Jul 2022 20:39:00 +0000 Subject: [PATCH 51/93] Bump version to 5.18.15 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 12 ++++++++++-- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eb9a6bcf..cd41e0c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,17 @@ # Change Log - + +## [v5.18.15](https://github.com/inspec/inspec/tree/v5.18.15) (2022-07-13) + +#### Merged Pull Requests +- Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) - + +### Changes since 5.18.14 release + +#### Merged Pull Requests +- Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index 00c93947c..e140359bb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.14 \ No newline at end of file +5.18.15 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 3de509642..74590666d 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.14".freeze + VERSION = "5.18.15".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 3fbd7d11a..4aa1e89fe 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.14".freeze + VERSION = "5.18.15".freeze end From e2f54ecf319b929df8a65517cc2c1664027fe97e Mon Sep 17 00:00:00 2001 From: Ian Maddaus Date: Wed, 20 Jul 2022 11:21:15 -0400 Subject: [PATCH 52/93] Delete azure_virtual_machine_data_disk.md page Signed-off-by: Ian Maddaus --- .../azure_virtual_machine_data_disk.md | 226 ------------------ 1 file changed, 226 deletions(-) delete mode 100644 docs-chef-io/content/inspec/resources/azure_virtual_machine_data_disk.md diff --git a/docs-chef-io/content/inspec/resources/azure_virtual_machine_data_disk.md b/docs-chef-io/content/inspec/resources/azure_virtual_machine_data_disk.md deleted file mode 100644 index 001290719..000000000 --- a/docs-chef-io/content/inspec/resources/azure_virtual_machine_data_disk.md +++ /dev/null @@ -1,226 +0,0 @@ -+++ -title = "azure_virtual_machine_data_disk resource" -draft = false -gh_repo = "inspec" -platform = "azure" - -[menu] - [menu.inspec] - title = "azure_virtual_machine_data_disk" - identifier = "inspec/resources/azure/azure_virtual_machine_data_disk.md azure_virtual_machine_data_disk resource" - parent = "inspec/resources/azure" -+++ - -Use this resource to ensure that a specific data disk attached to a machine has been created properly. - -## Availability - -### Installation - -{{% inspec/inspec_installation %}} - -### Version - -This resource first became available in v2.0.16 of InSpec. - -## Syntax - -The name of the resource group and machine are required to use this resource. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'MyVM') do - its('property') { should eq 'value' } - end - -where: - -- `MyVm` is the name of the virtual machine as seen in Azure. (It is **not** the hostname of the machine) -- `InSpec-Azure` is the name of the resource group that the machine is in. -- `property` is a resource property -- `value` is the expected output from the matcher - -## Examples - -The following examples show to use this Chef InSpec audit resource. - -### Check that the first data disk is of the correct size - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Linux-Internal-VM').where(number: 1) do - its('size') { should cmp >= 15 } - end - -## Resource Parameters - -- `group_name` -- `name` -- `apiversion` - -## Parameter Examples - -The options that can be passed to the resource are as follows. - -### `group_name` (required) - -Use this parameter to define the Azure Resource Group to be tested. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure') do - ... - end - -### name - -Use this parameter to define the name of the Azure resource to test. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM') do - ... - end - -### apiversion - -The API Version to use when querying the resource. Defaults to the latest version for the resource. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM', apiversion: '2.0') do - ... - end - -These options can also be set using the environment variables: - -- `AZURE_RESOURCE_GROUP_NAME` -- `AZURE_RESOURCE_NAME` -- `AZURE_RESOURCE_API_VERSION` - -When the options have been set as well as the environment variables, the environment variables take priority. - -## Filter Criteria - -- `number` -- `disk` - -## Filter Examples - -### disk - -The zero based index of the disk attached to the machine. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM').where(disk: 0) - end - -### number - -The '1' based index of the disk attached to the machine. - - describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM').where(number: 1) - end - -## Properties - -- `count`, `disk`, `number`, `name`, `size`, `lun`, `caching`, `create_option`, `is_managed_disk?`, `vhd_uri`, `storage_account_name`, `storage_account_type`, `id`, `subscription_id`, `resource_group` - -## Property Examples - -### count - -Returns the number of data disks attached to the machine - - its('count') { should eq 1 } - -### name - -Returns a string of the name of the disk. - - its('name') { should cmp 'linux-external-datadisk-1' } - -### size - -Returns an integer of size of this disk in GB. - - its('size') { should cmp >= 15 } - -### lun - -The disk number as reported by Azure. Has a zero-based index value. - - its('lun') { should cmp 0 } - -### caching - -String stating the caching that has been set on the disk. - - its('caching') { should cmp 'none' } - -### create_option - -How the disk was created. Typically for data disks, this will be the string value 'Empty'. - - its('create_option') { should cmp 'Empty' } - -### is_managed_disk? - -Boolean stating if the disk is a managed disk or not. If it is not a managed disk then it is one that is stored in a Storage Account. - - its('is_managed_disk?') { should cmp 'false' } - -### vhd_uri - -If this is _not_ a managed disk, then the `vhd_uri` will be the full URI to the disk in the storage account. - - its('vhd_uri') { should cmp 'https://primary_storage.blob.core.windows.net/container_name/vm_name.vhd' } - -### storage_account_name - -If this is _not_ a managed disk this will be the storage account name in which the disk is stored. - -This derived from the `vhd_uri`. - - its('storage_account_name') { should cmp 'primary_storage' } - -### storage_account_type - -If this is a managed disk this is the storage account type, e.g. `Standard_LRS`. - - its('storage_account_type') { should cmp 'Standard_LRS' } - -### id - -If this is a managed disk then this is the fully qualified id for the disk in Azure. - - its('id') { should cmp '/subscriptions/1234abcd-e567-890f-g123-456h78i9jkl0/resourceGroups/InSpec-Azure' } - -### subscription_id - -If this is a managed disk, this returns the subscription id of where the disk is stored. - -This is derived from the `id`. - - its('subscription_id') { should cmp '1234abcd-e567-890f-g123-456h78i9jkl0' } - -### resource_group - -If this is a managed disk, this returns the resource group in which the disk is stored. - -This is derived from the `id`. - - its('resource_group') { should cmp 'InSpec-Azure' } - -## Matchers - -This Chef InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). - -The following properties are applied to the virtual machine itself and not specific disks. - -### have_data_disks - -Returns a boolean denoting if any data disks are attached to the machine. - - it { should have_data_disks } - -### have_managed_disks - -Returns a boolean stating if the machine has Managed Disks for data disks. - - it { should have_managed_disks } - -## References - -- [Azure Ruby SDK - Compute](https://github.com/Azure/azure-sdk-for-ruby/tree/master/management/azure_mgmt_compute) -- [Linux Internal Data Disks](https://github.com/inspec/inspec/blob/main/test/integration/azure/verify/controls/virtual_machine_linux_external_vm_datadisk.rb) -- [Windows Internal Data Disk](https://github.com/inspec/inspec/blob/main/test/integration/azure/verify/controls/virtual_machine_windows_internal_vm_datadisk.rb) From 57eca85305e7542b1ae404911ce395c0a15680a6 Mon Sep 17 00:00:00 2001 From: Ian Maddaus Date: Wed, 20 Jul 2022 11:21:35 -0400 Subject: [PATCH 53/93] Spellcheck corrections Signed-off-by: Ian Maddaus --- docs-chef-io/content/inspec/cli.md | 2 +- docs-chef-io/content/inspec/profiles.md | 45 +++++++++--------- .../content/inspec/resources/auditd.md | 2 +- .../inspec/resources/azurerm_ad_users.md | 2 +- .../azurerm_cosmosdb_database_account.md | 2 +- .../azurerm_event_hub_authorization_rule.md | 8 ++-- .../resources/azurerm_event_hub_event_hub.md | 10 ++-- .../resources/azurerm_event_hub_namespace.md | 2 +- .../inspec/resources/azurerm_iothub.md | 2 +- ...azurerm_iothub_event_hub_consumer_group.md | 8 ++-- ...zurerm_iothub_event_hub_consumer_groups.md | 6 +-- .../inspec/resources/azurerm_load_balancer.md | 2 +- .../resources/azurerm_management_group.md | 4 +- .../resources/azurerm_mysql_databases.md | 2 +- .../inspec/resources/azurerm_mysql_server.md | 2 +- .../resources/azurerm_network_interface.md | 2 +- .../resources/azurerm_postgresql_server.md | 2 +- .../inspec/resources/azurerm_sql_databases.md | 2 +- .../inspec/resources/azurerm_sql_server.md | 2 +- .../inspec/resources/azurerm_subnet.md | 4 +- .../inspec/resources/azurerm_subnets.md | 2 +- .../resources/azurerm_virtual_network.md | 2 +- .../inspec/resources/azurerm_webapp.md | 2 +- .../content/inspec/resources/command.md | 4 +- docs-chef-io/content/inspec/resources/cpan.md | 2 +- docs-chef-io/content/inspec/resources/cron.md | 2 +- .../content/inspec/resources/crontab.md | 4 +- .../content/inspec/resources/docker.md | 6 +-- .../inspec/resources/docker_service.md | 8 ++-- .../content/inspec/resources/etc_hosts.md | 3 +- .../content/inspec/resources/filesystem.md | 2 +- docs-chef-io/content/inspec/resources/http.md | 2 +- .../content/inspec/resources/iis_app.md | 2 +- docs-chef-io/content/inspec/resources/ini.md | 2 +- .../content/inspec/resources/ipfilter.md | 2 +- .../content/inspec/resources/key_rsa.md | 20 ++++---- .../content/inspec/resources/mssql_session.md | 2 +- .../inspec/resources/oracledb_session.md | 22 ++++----- .../content/inspec/resources/passwd.md | 2 +- .../content/inspec/resources/postfix_conf.md | 2 +- .../inspec/resources/security_identifier.md | 4 +- .../content/inspec/resources/shadow.md | 2 +- .../content/inspec/resources/users.md | 2 +- .../inspec/resources/virtualization.md | 2 +- .../content/inspec/resources/windows_task.md | 4 +- .../inspec/resources/x509_certificate.md | 46 +++++++++---------- docs-chef-io/content/inspec/shell.md | 12 ++--- 47 files changed, 138 insertions(+), 138 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 0b9c5efff..f0ad9eac8 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -316,7 +316,7 @@ This subcommand has the following additional options: * `--config=CONFIG` Read configuration from JSON file (`-` reads from stdin). * `--controls=one two three` - A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests. + A list of control names to run, or a list of regular expressions to match against control names. Ignore all other tests. * `--create-lockfile`, `--no-create-lockfile` Write out a lockfile based on this execution (unless one already exists). * `--distinct-exit`, `--no-distinct-exit` diff --git a/docs-chef-io/content/inspec/profiles.md b/docs-chef-io/content/inspec/profiles.md index de0282590..0298fdcef 100644 --- a/docs-chef-io/content/inspec/profiles.md +++ b/docs-chef-io/content/inspec/profiles.md @@ -19,7 +19,7 @@ is a standalone structure with its own distribution and execution flow. A profile should have the following structure: -```YAML +```yaml examples/profile ├── README.md ├── controls @@ -67,7 +67,7 @@ Each profile must have an `inspec.yml` file that defines the following informati `name` is required; all other profile settings are optional. For example: -```YAML +```yaml name: ssh title: Basic SSH maintainer: Chef Software, Inc. @@ -89,7 +89,7 @@ inspec_version: "~> 2.1" The `inspec.yml` also supports embedded ERB in the file. For example: -```YAML +```yaml name: dummy title: InSpec Profile maintainer: The Authors @@ -130,7 +130,7 @@ platforms. The new families can restrict the platform family to `os`, `aws`, `az For example, to target anything running Debian Linux, use: -```YAML +```yaml name: ssh supports: - platform-name: debian @@ -138,7 +138,7 @@ supports: To target only Ubuntu version 20.04, use: -```YAML +```yaml name: ssh supports: - platform-name: ubuntu @@ -147,7 +147,7 @@ supports: To target the entire release of Ubuntu version 20.x, use: -```YAML +```yaml name: ssh supports: - platform-name: ubuntu @@ -156,7 +156,7 @@ supports: To target the Red Hat and derivative platforms such as CentOS and Oracle Linux, use: -```YAML +```yaml name: ssh supports: - platform-family: redhat @@ -164,7 +164,7 @@ supports: To target the entire Windows 2019 platform family, including Datacenter and Core Servers, use: -```YAML +```yaml name: ssh supports: - platform-name: windows_server_2019* @@ -172,7 +172,7 @@ supports: To target anything running on Amazon AWS, use: -```YAML +```yaml name: ssh supports: - platform: aws @@ -180,7 +180,7 @@ supports: To target all of these examples in a single `inspec.yml` file, use: -```YAML +```yaml name: ssh supports: - platform-name: debian @@ -206,7 +206,7 @@ needs to be specified in the including profile’s `inspec.yml` file in the `dep section. For each profile to be included, a location for the profile from where to be fetched and a name for the profile should be included. For example: -```YAML +```yaml depends: - name: linux-baseline url: https://github.com/dev-sec/linux-baseline/archive/master.tar.gz @@ -221,7 +221,7 @@ Chef InSpec supports a number of dependency sources. The `path` setting defines a profile that is located on disk. This setting is typically used during development of profiles and when debugging profiles. -```YAML +```yaml depends: - name: my-profile path: /absolute/path @@ -235,17 +235,17 @@ The `url` setting specifies a profile that is located at an HTTP- or HTTPS-based URL. The profile must be accessible via a HTTP GET operation and must be a valid profile archive (zip, tar, or tar.gz format). -```YAML +```yaml depends: - name: my-profile url: https://my.domain/path/to/profile.tgz - name: profile-via-git - url: https://github.com/myusername/myprofile-repo/archive/master.tar.gz + url: https://github.com/username/myprofile-repo/archive/master.tar.gz ``` `url` also supports basic authentication. -```YAML +```yaml depends: - name: my-profile url: https://my.domain/path/to/profile.tgz @@ -260,7 +260,7 @@ optional settings for branch, tag, commit, version, and relative_path. The sourc location is translated into a URL upon resolution. This type of dependency supports version constraints via semantic versioning as git tags. -```YAML +```yaml depends: - name: git-profile git: http://url/to/repo @@ -278,7 +278,7 @@ on Chef Supermarket. The source location is translated into a URL upon resolutio For example: -```YAML +```yaml depends: - name: supermarket-profile supermarket: supermarket-username/supermarket-profile @@ -293,7 +293,7 @@ or Chef Compliance server. For example: -```YAML +```yaml depends: - name: linux compliance: base/linux @@ -304,8 +304,7 @@ Any profile with ruby gem dependencies that need to be installed can be specifie For example, if you required any ruby library in a custom resource that needs a specific gem to be installed, then you can specify those gems in the metadata file. Chef InSpec will prompt to install the gems to `~/.inspec/gems` when you run your profile the first time. To skip the prompt and automatically install, pass the `--auto-install-gems` option to `inspec exec`. - -```YAML +```yaml gem_dependencies: - name: "mongo" version: ">= 2.3.12" @@ -412,7 +411,7 @@ for use in your profile. If two of your dependencies provide a resource with the same name, you can use the `require_resource` DSL function to disambiguate the two: -```YAML +```yaml require_resource(profile: 'my_dep', resource: 'my_res', as: 'my_res2') ``` @@ -436,7 +435,7 @@ of a profile. They are accessed by their name relative to this folder with Here is an example for reading and testing a list of ports. The folder structure is: -```YAML +```yaml examples/profile ├── controls │ ├── example.rb @@ -447,7 +446,7 @@ examples/profile With `services.yml` containing: -```YAML +```yaml - service_name: httpd-alpha port: 80 - service_name: httpd-beta diff --git a/docs-chef-io/content/inspec/resources/auditd.md b/docs-chef-io/content/inspec/resources/auditd.md index eec369226..6d90aa565 100644 --- a/docs-chef-io/content/inspec/resources/auditd.md +++ b/docs-chef-io/content/inspec/resources/auditd.md @@ -11,7 +11,7 @@ platform = "linux" parent = "inspec/resources/os" +++ -Use the `auditd` Chef InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files. These rules are output using the auditctl -l command. This resource supports versions of `audit` >= 2.3. +Use the `auditd` Chef InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files. These rules are output using the `auditctl -l` command. This resource supports versions of `audit` >= 2.3. ## Availability diff --git a/docs-chef-io/content/inspec/resources/azurerm_ad_users.md b/docs-chef-io/content/inspec/resources/azurerm_ad_users.md index e69224ba3..1e9d37124 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_ad_users.md +++ b/docs-chef-io/content/inspec/resources/azurerm_ad_users.md @@ -82,7 +82,7 @@ The following examples show how to use this InSpec audit resource. Filters the results to include only those Users that match the given name. This is a string value. - describe azurerm_ad_users.where{ displayName.eql?('Joe Bloggs') } do + describe azurerm_ad_users.where{ displayName.eql?('Haris Shefu') } do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_cosmosdb_database_account.md b/docs-chef-io/content/inspec/resources/azurerm_cosmosdb_database_account.md index 5ceb3791e..94a46310b 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_cosmosdb_database_account.md +++ b/docs-chef-io/content/inspec/resources/azurerm_cosmosdb_database_account.md @@ -104,7 +104,7 @@ Indicates the type of database account, e.g. `GlobalDocumentDB`, `MongoDB` ### tags -Resource tags applied to the ComsosDb Account. +Resource tags applied to the Cosmos DB Account. ### properties diff --git a/docs-chef-io/content/inspec/resources/azurerm_event_hub_authorization_rule.md b/docs-chef-io/content/inspec/resources/azurerm_event_hub_authorization_rule.md index 02d987fa4..5fb6e7492 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_event_hub_authorization_rule.md +++ b/docs-chef-io/content/inspec/resources/azurerm_event_hub_authorization_rule.md @@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack The `resource_group`, `namespace_name`, `event_hub_name` and `authorization_rule_name` must be given as a parameter. - describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name: 'myeventhub', authorization_rule_name: 'my-auth-rule') do + describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'event-hub-namespace', event_hub_name: 'event-hub', authorization_rule_name: 'my-auth-rule') do it { should exist } end @@ -57,13 +57,13 @@ The `resource_group`, `namespace_name`, `event_hub_name` and `authorization_rule If an Event Hub Authorization Rule is referenced with a valid `Resource Group`, `Namespace Name`, `Event Hub Name` and `Authorization Rule Name` - describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_endpoint: 'myeventhub', authorization_rule: 'my-auth-rule') do + describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name: 'event-hub-namespace', event_hub_endpoint: 'event-hub', authorization_rule: 'my-auth-rule') do it { should exist } end If a Event Hub Authorization Rule is referenced with an invalid `Resource Group`, `Namespace Name`, `Event Hub Name` or `Authorization Rule Name` - describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist', event_hub_endpoint: 'fakeendpoint', authorization_rule: 'fake-auth-rule') do + describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist', event_hub_endpoint: 'fake-endpoint', authorization_rule: 'fake-auth-rule') do it { should_not exist } end @@ -117,7 +117,7 @@ requests are always welcome. ### exists - describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name: 'myeventhub', authorization_rule_name: 'my-auth-rule') do + describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'event-hub-namespace', event_hub_name: 'event-hub', authorization_rule_name: 'my-auth-rule') do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_event_hub_event_hub.md b/docs-chef-io/content/inspec/resources/azurerm_event_hub_event_hub.md index 67e9514f3..c86e843b6 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_event_hub_event_hub.md +++ b/docs-chef-io/content/inspec/resources/azurerm_event_hub_event_hub.md @@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack The `resource_group`, `namespace_name` and `event_hub_name` must be given as a parameter. - describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name 'myeventhub') do + describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name 'event-hub') do it { should exist } end @@ -57,13 +57,13 @@ The `resource_group`, `namespace_name` and `event_hub_name` must be given as a p If an Event Hub Event Hub is referenced with a valid `Resource Group`, `Namespace Name` and `Event Hub Name` - describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name 'myeventhub') do + describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name 'event-hub') do it { should exist } end If a Event Hub Event Hub is referenced with an invalid `Resource Group`, `Namespace Name` and `Event Hub Name` - describe azurerm_event_hub_event_hub(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist', event_hub_name 'i-dont-exist') do + describe azurerm_event_hub_event_hub(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist', event_hub_name 'i-do-not-exist') do it { should_not exist } end @@ -86,7 +86,7 @@ Azure resource ID. ### name -Event Hub name, e.g. `myeventhub`. +Event Hub name, e.g. `event-hub`. ### type @@ -116,7 +116,7 @@ requests are always welcome. ### exists - describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name: 'myeventhub') do + describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name: 'event-hub') do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_event_hub_namespace.md b/docs-chef-io/content/inspec/resources/azurerm_event_hub_namespace.md index 187ae7192..9f3087290 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_event_hub_namespace.md +++ b/docs-chef-io/content/inspec/resources/azurerm_event_hub_namespace.md @@ -63,7 +63,7 @@ If an Event Hub Namespace is referenced with a valid `Resource Group` and `Names If an Event Hub Namespace is referenced with an invalid `Resource Group` or `Namespace Name` - describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist') do + describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_iothub.md b/docs-chef-io/content/inspec/resources/azurerm_iothub.md index adbbecc67..9b96fc186 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_iothub.md +++ b/docs-chef-io/content/inspec/resources/azurerm_iothub.md @@ -63,7 +63,7 @@ If an IoT Hub is referenced with a valid `Resource Group` and `Resource Name` If an IoT Hub is referenced with an invalid `Resource Group` or `Resource Name` - describe azurerm_iothub(resource_group: 'invalid-rg', resource_name: 'i-dont-exist') do + describe azurerm_iothub(resource_group: 'invalid-rg', resource_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_group.md b/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_group.md index a23bab423..6823ad40f 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_group.md +++ b/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_group.md @@ -50,7 +50,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack The `resource_group`, `resource_name`, `event_hub_endpoint` and `consumer_group` must be given as a parameter. - describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do + describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do it { should exist } end @@ -58,13 +58,13 @@ The `resource_group`, `resource_name`, `event_hub_endpoint` and `consumer_group` If an IoT Hub Event Hub Consumer Group is referenced with a valid `Resource Group`, `Resource Name`, `Event Hub Endpoint` and `Consumer Group` - describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do + describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do it { should exist } end If an IoT Hub Event Hub Consumer Group is referenced with an invalid `Resource Group`, `Resource Name`, `Event Hub Endpoint` or `Consumer Group` - describe azurerm_iothub_event_hub_consumer_group(resource_group: 'invalid-rg', resource_name: 'invalid-resource', event_hub_endpoint: 'invalideventhub', consumer_group: 'invalid-consumer-group') do + describe azurerm_iothub_event_hub_consumer_group(resource_group: 'invalid-rg', resource_name: 'invalid-resource', event_hub_endpoint: 'invalid-event-hub', consumer_group: 'invalid-consumer-group') do it { should_not exist } end @@ -123,7 +123,7 @@ requests are always welcome. ### exists - describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do + describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_groups.md b/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_groups.md index b78b3c85c..3dead3273 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_groups.md +++ b/docs-chef-io/content/inspec/resources/azurerm_iothub_event_hub_consumer_groups.md @@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack The `resource_group`, `resource_name` and `event_hub_endpoint` must be given as a parameter. - describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do + describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do its('names') { should include "my-consumer-group"} its('types') { should include 'Microsoft.Devices/IotHubs/EventHubEndpoints/ConsumerGroups' } end @@ -58,7 +58,7 @@ The `resource_group`, `resource_name` and `event_hub_endpoint` must be given as If a IoT Hub Event Hub Consumer Groups is referenced with a valid `Resource Group`, `Resource Name` and `Event Hub Endpoint` - describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do + describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do it { should exist } end @@ -126,7 +126,7 @@ requests are always welcome. ### exists - describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do + describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_load_balancer.md b/docs-chef-io/content/inspec/resources/azurerm_load_balancer.md index d33f12cef..b5d010ba4 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_load_balancer.md +++ b/docs-chef-io/content/inspec/resources/azurerm_load_balancer.md @@ -63,7 +63,7 @@ If a Load Balancer is referenced with a valid `Resource Group` and `Load balance If a Load Balancer is referenced with an invalid `Resource Group` or `Load balancer Name` - describe azurerm_load_balancer(resource_group: 'invalid-rg', loadbalancer_name: 'i-dont-exist') do + describe azurerm_load_balancer(resource_group: 'invalid-rg', loadbalancer_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_management_group.md b/docs-chef-io/content/inspec/resources/azurerm_management_group.md index 6a5aca62f..2e1f57569 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_management_group.md +++ b/docs-chef-io/content/inspec/resources/azurerm_management_group.md @@ -256,12 +256,12 @@ requests are always welcome. ### exists # If a management group is found it will exist - describe azurerm_management_group(groupd_id: 'MyGroupId') do + describe azurerm_management_group(group_id: 'MyGroupId') do it { should exist } end # management groups that aren't found will not exist - describe azurerm_management_group(groupd_id: 'DoesNotExist') do + describe azurerm_management_group(group_id: 'DoesNotExist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_mysql_databases.md b/docs-chef-io/content/inspec/resources/azurerm_mysql_databases.md index 74c7eb74f..2ab3a29a9 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_mysql_databases.md +++ b/docs-chef-io/content/inspec/resources/azurerm_mysql_databases.md @@ -46,7 +46,7 @@ This resource first became available in 1.6.0 of the inspec-azure resource pack. ## Syntax -An `azurerm_mysql_databases` resource block returns all MySQL Databases on a MySQL Server, within a Rsource Group. +An `azurerm_mysql_databases` resource block returns all MySQL Databases on a MySQL Server, within a resource group. describe azurerm_mysql_databases(resource_group: ..., server_name: ...) do ... diff --git a/docs-chef-io/content/inspec/resources/azurerm_mysql_server.md b/docs-chef-io/content/inspec/resources/azurerm_mysql_server.md index fac816de9..99f86c05c 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_mysql_server.md +++ b/docs-chef-io/content/inspec/resources/azurerm_mysql_server.md @@ -63,7 +63,7 @@ If a SQL Server is referenced with a valid `Resource Group` and `Server Name` If a SQL Server is referenced with an invalid `Resource Group` or `Server Name` - describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do + describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_network_interface.md b/docs-chef-io/content/inspec/resources/azurerm_network_interface.md index 778edc7fa..f26eec141 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_network_interface.md +++ b/docs-chef-io/content/inspec/resources/azurerm_network_interface.md @@ -62,7 +62,7 @@ If a Network Interface is referenced with a valid `Resource Group` and `Name` If a Network Interface is referenced with an invalid `Resource Group` or `Name` - describe azurerm_network_interface(resource_group: 'invalid-rg', name: 'i-dont-exist') do + describe azurerm_network_interface(resource_group: 'invalid-rg', name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_postgresql_server.md b/docs-chef-io/content/inspec/resources/azurerm_postgresql_server.md index df89f0139..6636d2f66 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_postgresql_server.md +++ b/docs-chef-io/content/inspec/resources/azurerm_postgresql_server.md @@ -63,7 +63,7 @@ If a PostgreSQL Server is referenced with a valid `Resource Group` and `Server N If a PostgreSQL Server is referenced with an invalid `Resource Group` or `Server Name` - describe azurerm_postgresql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do + describe azurerm_postgresql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_sql_databases.md b/docs-chef-io/content/inspec/resources/azurerm_sql_databases.md index 7b3641ef7..9a68ce00b 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_sql_databases.md +++ b/docs-chef-io/content/inspec/resources/azurerm_sql_databases.md @@ -46,7 +46,7 @@ This resource first became available in 1.2.0 of the inspec-azure resource pack. ## Syntax -An `azurerm_sql_databases` resource block returns all SQL Databases on a SQL Server, within a Rsource Group. +An `azurerm_sql_databases` resource block returns all SQL Databases on a SQL Server, within a resource group. describe azurerm_sql_databases(resource_group: ..., server_name: ...) do ... diff --git a/docs-chef-io/content/inspec/resources/azurerm_sql_server.md b/docs-chef-io/content/inspec/resources/azurerm_sql_server.md index 7c23e13aa..2bb603db0 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_sql_server.md +++ b/docs-chef-io/content/inspec/resources/azurerm_sql_server.md @@ -63,7 +63,7 @@ If a SQL Server is referenced with a valid `Resource Group` and `Server Name` If a SQL Server is referenced with an invalid `Resource Group` or `Server Name` - describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do + describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do it { should_not exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_subnet.md b/docs-chef-io/content/inspec/resources/azurerm_subnet.md index 92ddb8865..b0ab59c1b 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_subnet.md +++ b/docs-chef-io/content/inspec/resources/azurerm_subnet.md @@ -123,13 +123,13 @@ The subnet's id. Id will be in format: - '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Inspec-Azure-mmclane/providers/Microsoft.Network/virtualNetworks/Inspec-VNet/subnets/Inspec-Subnet' + '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/Inspec-VNet/subnets/Inspec-Subnet' ### name The subnets's name. - its('name') { should eq('MySubnetName') } + its('name') { should eq('SubnetName') } ### type diff --git a/docs-chef-io/content/inspec/resources/azurerm_subnets.md b/docs-chef-io/content/inspec/resources/azurerm_subnets.md index 8cb9f88f5..2d1d2ed86 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_subnets.md +++ b/docs-chef-io/content/inspec/resources/azurerm_subnets.md @@ -55,7 +55,7 @@ The `resource_group` and 'vnet' must be given as a parameter. ## Examples - # Exists if any subnetss exist for a given virtual network in the resource group + # Exists if any subnets exist for a given virtual network in the resource group describe azurerm_subnets(resource_group: 'MyResourceGroup', vnet: 'MyVnetName') do it { should exist } end diff --git a/docs-chef-io/content/inspec/resources/azurerm_virtual_network.md b/docs-chef-io/content/inspec/resources/azurerm_virtual_network.md index c3d737788..116b375d8 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_virtual_network.md +++ b/docs-chef-io/content/inspec/resources/azurerm_virtual_network.md @@ -121,7 +121,7 @@ The virtual network's id. Id will be in format: - '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Inspec-Azure-mmclane/providers/Microsoft.Network/virtualNetworks/MyVnetName' + '/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/MyVnetName' ### name diff --git a/docs-chef-io/content/inspec/resources/azurerm_webapp.md b/docs-chef-io/content/inspec/resources/azurerm_webapp.md index a46207fbd..e660d0cfc 100644 --- a/docs-chef-io/content/inspec/resources/azurerm_webapp.md +++ b/docs-chef-io/content/inspec/resources/azurerm_webapp.md @@ -100,7 +100,7 @@ The Resource Group as well as the Webapp name. - `auth_settings` - `configuration` -All of the attributes are avialable via dot notation. This is an example of the currently available attributes. +All of the attributes are available via dot notation. This is an example of the currently available attributes. ```ruby control 'azurerm_webapp' do diff --git a/docs-chef-io/content/inspec/resources/command.md b/docs-chef-io/content/inspec/resources/command.md index 7b851d30d..ed498e7c3 100644 --- a/docs-chef-io/content/inspec/resources/command.md +++ b/docs-chef-io/content/inspec/resources/command.md @@ -167,7 +167,7 @@ By default the command that is ran is shown in the Chef InSpec output. This can The following examples show how to use `redact_regex`: # Example without capture groups - describe command('myapp -p secretpassword -d no_redact', redact_regex: /-p .* -d/) do + describe command('myapp -p secret_password -d no_redact', redact_regex: /-p .* -d/) do its('exit_status') { should cmp 0 } end @@ -178,7 +178,7 @@ The following examples show how to use `redact_regex`: # Example with capture groups # Each set of parenthesis is a capture group. # Anything in the two capture groups will not be 'REDACTED' - describe command('myapp -p secretpassword -d no_redact', redact_regex: /(-p ).*( -d)/) do + describe command('myapp -p secret_password -d no_redact', redact_regex: /(-p ).*( -d)/) do its('exit_status') { should cmp 0 } end diff --git a/docs-chef-io/content/inspec/resources/cpan.md b/docs-chef-io/content/inspec/resources/cpan.md index ae85e2865..d0752e118 100644 --- a/docs-chef-io/content/inspec/resources/cpan.md +++ b/docs-chef-io/content/inspec/resources/cpan.md @@ -60,7 +60,7 @@ This resource uses package names and perl library paths as resource parameters. Hint: You can pass multiple paths separated with a colon `/path/to/perl5/lib:/usr/share/perl5/vendor_perl/lib/perl5` - describe cpan('DBD::Pg', '/home/jdoe/perl5/lib/perl5') do + describe cpan('DBD::Pg', '/home/username/perl5/lib/perl5') do it { should be_installed } end diff --git a/docs-chef-io/content/inspec/resources/cron.md b/docs-chef-io/content/inspec/resources/cron.md index 69ab1df81..c27fe9273 100644 --- a/docs-chef-io/content/inspec/resources/cron.md +++ b/docs-chef-io/content/inspec/resources/cron.md @@ -55,7 +55,7 @@ The following examples show how to use this audit resource. it { should have_entry "5 * * * * /some/scheduled/task.sh" } end -### Test to ensure myuser's crontab has a particular cron entry +### Test to ensure a user's crontab has a particular cron entry describe cron('MY_USER') do it { should have_entry "5 * * * * /some/scheduled/task.sh" } diff --git a/docs-chef-io/content/inspec/resources/crontab.md b/docs-chef-io/content/inspec/resources/crontab.md index c30e50358..1dd99dcb5 100644 --- a/docs-chef-io/content/inspec/resources/crontab.md +++ b/docs-chef-io/content/inspec/resources/crontab.md @@ -55,9 +55,9 @@ The following examples show how to use this Chef InSpec audit resource. its('commands') { should include '/path/to/some/script -option arg' } end -### Test that myuser's crontab entry for command '/home/myuser/build.sh' runs every minute +### Test that username's crontab entry for command '/home/username/build.sh' runs every minute - describe crontab('myuser').commands('/home/myuser/build.sh') do + describe crontab('username').commands('/home/username/build.sh') do its('hours') { should cmp '*' } its('minutes') { should cmp '*' } end diff --git a/docs-chef-io/content/inspec/resources/docker.md b/docs-chef-io/content/inspec/resources/docker.md index 6cc96dc68..ad0d81cb2 100644 --- a/docs-chef-io/content/inspec/resources/docker.md +++ b/docs-chef-io/content/inspec/resources/docker.md @@ -25,7 +25,7 @@ This resource first became available in v1.21.0 of InSpec. ## Syntax -A `docker` resource block declares allows you to write test for many containers: +A `docker` resource block allows you to write tests for many containers: describe docker.containers do its('images') { should_not include 'u12:latest' } @@ -33,7 +33,7 @@ A `docker` resource block declares allows you to write test for many containers: or: - describe docker.containers.where { names == 'flamboyant_colden' } do + describe docker.containers.where { names == 'flamboyant_allen' } do it { should be_running } end @@ -45,7 +45,7 @@ where The `docker` resource block also declares allows you to write test for many images: describe docker.images do - its('repositories') { should_not include 'inssecure_image' } + its('repositories') { should_not include 'insecure_image' } end or if you want to query specific images: diff --git a/docs-chef-io/content/inspec/resources/docker_service.md b/docs-chef-io/content/inspec/resources/docker_service.md index f3f0da345..3323852f8 100644 --- a/docs-chef-io/content/inspec/resources/docker_service.md +++ b/docs-chef-io/content/inspec/resources/docker_service.md @@ -29,7 +29,7 @@ A `docker_service` resource block declares the service by name: describe docker_service('foo') do it { should exist } - its('id') { should eq '2ghswegspre1' } + its('id') { should eq 'docker-service-id' } its('repo') { should eq 'alpine' } its('tag') { should eq 'latest' } end @@ -38,7 +38,7 @@ A `docker_service` resource block declares the service by name: The resource allows you to pass in a service id: - describe docker_service(id: '2ghswegspre1') do + describe docker_service(id: 'docker-service-id') do ... end @@ -56,7 +56,7 @@ The following examples show how to use Chef InSpec `docker_service` resource. The `id` property returns the service id: - its('id') { should eq '2ghswegspre1' } + its('id') { should eq 'docker-service-id' } ### image @@ -104,7 +104,7 @@ The `tag` property tests the value of image tag: describe docker_service('foo') do it { should exist } - its('id') { should eq '2ghswegspre1' } + its('id') { should eq 'docker-service-id' } its('repo') { should eq 'alpine' } its('tag') { should eq 'latest' } end diff --git a/docs-chef-io/content/inspec/resources/etc_hosts.md b/docs-chef-io/content/inspec/resources/etc_hosts.md index 6b2c70e2a..e3016166d 100644 --- a/docs-chef-io/content/inspec/resources/etc_hosts.md +++ b/docs-chef-io/content/inspec/resources/etc_hosts.md @@ -77,7 +77,8 @@ The `all_host_names` property returns a two-dimensional string array where each its('ip_address') { should cmp '127.0.1.154' } end -### Test the primay name for where ip address is '::1' +### Test the primary name for where IP address is '::1' + describe etc_hosts.where { ip_address == '::1' } do its('primary_name') { should cmp 'localhost' } end diff --git a/docs-chef-io/content/inspec/resources/filesystem.md b/docs-chef-io/content/inspec/resources/filesystem.md index 70283a575..5e9463739 100644 --- a/docs-chef-io/content/inspec/resources/filesystem.md +++ b/docs-chef-io/content/inspec/resources/filesystem.md @@ -61,7 +61,7 @@ The `free_kb` property returns the size of available space on the partition in k its('size_kb') { should be >= 32000 } -## percent_free (Integrer) +## percent_free (Integer) The `percent_free` property returns the available free space on the partition, ranges from 0 to 100. diff --git a/docs-chef-io/content/inspec/resources/http.md b/docs-chef-io/content/inspec/resources/http.md index edac6b1a4..08d7b8912 100644 --- a/docs-chef-io/content/inspec/resources/http.md +++ b/docs-chef-io/content/inspec/resources/http.md @@ -180,7 +180,7 @@ You can include the username and password in the `proxy` parameter: The `proxy` parameter also accepts proxy options in hash format: - describe http('http://localhost:8080/ping', proxy: { uri: 'http://www.example.com:3128', user: 'username', password: 'proxypassword'}) do + describe http('http://localhost:8080/ping', proxy: { uri: 'http://www.example.com:3128', user: 'username', password: 'proxy-password'}) do ... end diff --git a/docs-chef-io/content/inspec/resources/iis_app.md b/docs-chef-io/content/inspec/resources/iis_app.md index cfe60e169..712293e06 100644 --- a/docs-chef-io/content/inspec/resources/iis_app.md +++ b/docs-chef-io/content/inspec/resources/iis_app.md @@ -70,7 +70,7 @@ For example: `physical_path` property returns the physical path of the application, such as `'C:\\inetpub\\wwwroot\\myapp'`. - its('phyiscal_path') { should eq 'C:\\inetpub\\wwwroot\\myapp' } + its('physical_path') { should eq 'C:\\inetpub\\wwwroot\\myapp' } ### protocols diff --git a/docs-chef-io/content/inspec/resources/ini.md b/docs-chef-io/content/inspec/resources/ini.md index 94c4a1bac..6d0cf6a40 100644 --- a/docs-chef-io/content/inspec/resources/ini.md +++ b/docs-chef-io/content/inspec/resources/ini.md @@ -55,7 +55,7 @@ Settings inside of sections, such as the following: In the event a section or setting name has a period in it, the alternate syntax can be used: - its(['section.with.a.dot.in.it', 'setting.name.with.dots']) { should cmp 'lotsadots' } + its(['section.with.a.dot.in.it', 'setting.name.with.dots']) { should cmp 'lots-of-dots' } ## Properties diff --git a/docs-chef-io/content/inspec/resources/ipfilter.md b/docs-chef-io/content/inspec/resources/ipfilter.md index c633b68e1..91cd47a79 100644 --- a/docs-chef-io/content/inspec/resources/ipfilter.md +++ b/docs-chef-io/content/inspec/resources/ipfilter.md @@ -69,6 +69,6 @@ For a full list of available matchers, please visit our [matchers page](/inspec/ ### have_rule -The `have_rule` matcher tests the named rule against the information in the output rule of `'ipftstat -io'`: +The `have_rule` matcher tests the named rule against the information in the output rule of `'ipfstat -io'`: it { should have_rule("RULE") } diff --git a/docs-chef-io/content/inspec/resources/key_rsa.md b/docs-chef-io/content/inspec/resources/key_rsa.md index 68b359ef8..9bb90a89a 100644 --- a/docs-chef-io/content/inspec/resources/key_rsa.md +++ b/docs-chef-io/content/inspec/resources/key_rsa.md @@ -29,16 +29,16 @@ This resource first became available in v1.18.0 of InSpec. An `key_rsa` resource block declares a `key file` to be tested. - describe key_rsa('mycertificate.key') do + describe key_rsa('certificate.key') do it { should be_private } it { should be_public } - its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982" } + its('public_key') { should match "PUBLIC_KEY" } its('key_length') { should eq 2048 } end You can use an optional passphrase with `key_rsa` - describe key_rsa('mycertificate.key', 'passphrase') do + describe key_rsa('certificate.key', 'passphrase') do it { should be_private } end @@ -48,23 +48,23 @@ You can use an optional passphrase with `key_rsa` The `public_key` property returns the public part of the RSA key pair - describe key_rsa('/etc/pki/www.mywebsite.com.key') do - its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982......" } + describe key_rsa('/etc/pki/www.example.com.key') do + its('public_key') { should match "RSA_PUBLIC_KEY" } end ### private_key (String) The `private_key` property returns the private key or the RSA key pair. - describe key_rsa('/etc/pki/www.mywebsite.com.key') do - its('private_key') { should match "-----BEGIN RSA PRIVATE KEY-----\nMIIJJwIBAAK......" } + describe key_rsa('/etc/pki/www.example.com.key') do + its('private_key') { should match "RSA_PRIVATE_KEY" } end ### key_length The `key_length` property allows testing the number of bits in the key pair. - describe key_rsa('/etc/pki/www.mywebsite.com.key') do + describe key_rsa('/etc/pki/www.example.com.key') do its('key_length') { should eq 2048 } end @@ -76,7 +76,7 @@ For a full list of available matchers, please visit our [matchers page](/inspec/ To verify if a key is public use the following: - describe key_rsa('/etc/pki/www.mywebsite.com.key') do + describe key_rsa('/etc/pki/www.example.com.key') do it { should be_public } end @@ -84,6 +84,6 @@ To verify if a key is public use the following: This property verifies that the key includes a private key: - describe key_rsa('/etc/pki/www.mywebsite.com.key') do + describe key_rsa('/etc/pki/www.example.com.key') do it { should be_private } end diff --git a/docs-chef-io/content/inspec/resources/mssql_session.md b/docs-chef-io/content/inspec/resources/mssql_session.md index 703d69a97..64dddbd84 100644 --- a/docs-chef-io/content/inspec/resources/mssql_session.md +++ b/docs-chef-io/content/inspec/resources/mssql_session.md @@ -59,7 +59,7 @@ The following examples show how to use this Chef InSpec audit resource. ### Test a specific host and instance - sql = mssql_session(user: 'my_user', password: 'password', host: 'mssqlserver', instance: 'foo') + sql = mssql_session(user: 'my_user', password: 'password', host: 'ms-sql-server', instance: 'foo') describe sql.query("SELECT SERVERPROPERTY('ProductVersion') as result").row(0).column('result') do its("value") { should cmp > '12.00.4457' } diff --git a/docs-chef-io/content/inspec/resources/oracledb_session.md b/docs-chef-io/content/inspec/resources/oracledb_session.md index 0720a68e3..1d5ac7978 100644 --- a/docs-chef-io/content/inspec/resources/oracledb_session.md +++ b/docs-chef-io/content/inspec/resources/oracledb_session.md @@ -25,15 +25,15 @@ This resource first became available in v1.0.0 of InSpec. ## Syntax -A `oracledb_session` resource block declares the username and password to use for the session with an optional service to connect to, and then the command to be run: +A `oracledb_session` resource block declares the username and PASSWORD to use for the session with an optional service to connect to, and then the command to be run: - describe oracledb_session(user: 'username', password: 'password', service: 'ORCL.localdomain').query('QUERY').row(0).column('result') do + describe oracledb_session(user: 'username', PASSWORD: 'PASSWORD', service: 'ORCL.localdomain').query('QUERY').row(0).column('result') do its('value') { should eq('') } end where -- `oracledb_session` declares a username and password with permission to run the query (required), and an optional parameters for host (default: `localhost`), SID (default: `nil`, which uses the default SID, and path to the sqlplus binary (default: `sqlplus`). +- `oracledb_session` declares a username and PASSWORD with permission to run the query (required), and an optional parameters for host (default: `localhost`), system identifier (SID) (default: `nil`), which uses the default SID, and path to the sqlplus binary (default: `sqlplus`). - it is possible to run queries as sysdba/sysoper by using `as_db_role option`, see examples - SQLcl can be used in place of sqlplus. Use the `sqlcl_bin` option to set the sqlcl binary path instead of `sqlplus_bin`. - `query('QUERY')` contains the query to be run @@ -51,7 +51,7 @@ The following examples show how to use this Chef InSpec audit resource. ### Test for matching databases - sql = oracledb_session(user: 'my_user', pass: 'password') + sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD') describe sql.query('SELECT NAME AS VALUE FROM v$database;').row(0).column('value') do its('value') { should cmp 'ORCL' } @@ -59,7 +59,7 @@ The following examples show how to use this Chef InSpec audit resource. ### Test for matching databases with custom host, SID and sqlplus binary location - sql = oracledb_session(user: 'my_user', pass: 'password', host: 'oraclehost', sid: 'mysid', sqlplus_bin: '/u01/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus') + sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', host: 'ORACLE_HOST', sid: 'ORACLE_SID', sqlplus_bin: '/u01/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus') describe sql.query('SELECT NAME FROM v$database;').row(0).column('name') do its('value') { should cmp 'ORCL' } @@ -67,9 +67,9 @@ The following examples show how to use this Chef InSpec audit resource. ### Test for table contains a specified value in any row for the given column name - sql = oracledb_session(user: 'my_user', pass: 'password', service: 'MYSID') + sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', service: 'ORACLE_SID') - describe sql.query('SELECT * FROM my_table;').column('my_column') do + describe sql.query('SELECT * FROM my_table;').column('COLUMN') do it { should include 'my_value' } end @@ -77,16 +77,16 @@ The following examples show how to use this Chef InSpec audit resource. The check will change user (with su) to specified user and run 'sqlplus / as sysdba' (sysoper, sysasm) - sql = oracledb_session(as_os_user: 'oracle', as_db_role: 'sysdba', service: 'MYSID') + sql = oracledb_session(as_os_user: 'oracle', as_db_role: 'sysdba', service: 'ORACLE_SID') describe sql.query('SELECT tablespace_name AS name FROM dba_tablespaces;').column('name') do - it { should include 'MYTABLESPACE' } + it { should include 'TABLE_SPACE' } end NOTE: option `as_os_user` available only on unix-like systems and not supported on Windows. Also this option requires that you are running inspec as `root` or with `--sudo` ### Test number of rows in the query result - sql = oracledb_session(user: 'my_user', pass: 'password') + sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD') describe sql.query('SELECT * FROM my_table;').rows do its('count') { should eq 20 } @@ -94,7 +94,7 @@ The following examples show how to use this Chef InSpec audit resource. ### Use data out of (remote) DB query to build other tests - sql = oracledb_session(user: 'my_user', pass: 'password', host: 'my.remote.db', service: 'MYSID') + sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', host: 'my.remote.db', service: 'ORACLE_SID') sql.query('SELECT * FROM files;').rows.each do |file_row| describe file(file_row['path']) do diff --git a/docs-chef-io/content/inspec/resources/passwd.md b/docs-chef-io/content/inspec/resources/passwd.md index 5cc21a261..5abe103b4 100644 --- a/docs-chef-io/content/inspec/resources/passwd.md +++ b/docs-chef-io/content/inspec/resources/passwd.md @@ -58,7 +58,7 @@ where ### gids -The `gids` property tests if the group indentifiers in the test match group identifiers in `/etc/passwd`: +The `gids` property tests if the group identifiers in the test match group identifiers in `/etc/passwd`: its('gids') { should include 1234 } its('gids') { should cmp 0 } diff --git a/docs-chef-io/content/inspec/resources/postfix_conf.md b/docs-chef-io/content/inspec/resources/postfix_conf.md index 092bf52a3..0868e87bb 100644 --- a/docs-chef-io/content/inspec/resources/postfix_conf.md +++ b/docs-chef-io/content/inspec/resources/postfix_conf.md @@ -42,7 +42,7 @@ When using `postfix_conf` with a custom configuration directory, the following s where -- `'path'` is the path to your Postfix configuration (ex. '/etc/my/postfix/path/main.cf') +- `'path'` is the path to your Postfix configuration (ex. '/etc/path/to/postfix/main.cf') ## Properties diff --git a/docs-chef-io/content/inspec/resources/security_identifier.md b/docs-chef-io/content/inspec/resources/security_identifier.md index 133155f22..ae166f57b 100644 --- a/docs-chef-io/content/inspec/resources/security_identifier.md +++ b/docs-chef-io/content/inspec/resources/security_identifier.md @@ -30,13 +30,13 @@ A `security_identifier` resource should specify the name and type of the trustee where - `group:` specifies that `'Everyone'` should be a group. `user:` can be used to specify a user account. - \*\* It is necessary to declare the type of the trustee because Windows allows users, groups and other entities to share names. If you really need to not specify the type, `unspecified:` can be used. This will attempt to match the name to a group and then a useraccount. This may take longer to execute and comes with the risk of Chef InSpec matching the name to an unintended trustee. + \*\* It is necessary to declare the type of the trustee because Windows allows users, groups and other entities to share names. If you really need to not specify the type, `unspecified:` can be used. This will attempt to match the name to a group and then a user account. This may take longer to execute and comes with the risk of Chef InSpec matching the name to an unintended trustee. ## Examples The following examples show how to use this Chef InSpec resource. -### Verify that the Admnistrator user has a SID +### Verify that the Administrator user has a SID describe security_identifier(user: 'Administrator') do it { should exist } diff --git a/docs-chef-io/content/inspec/resources/shadow.md b/docs-chef-io/content/inspec/resources/shadow.md index 9db7f9be2..61df02c2a 100644 --- a/docs-chef-io/content/inspec/resources/shadow.md +++ b/docs-chef-io/content/inspec/resources/shadow.md @@ -26,7 +26,7 @@ The format for `/etc/shadow` includes: These entries are defined as a colon-delimited row in the file, one row per user: - dannos:Gb7crrO5CDF.:10063:0:99999:7::: + username:Gb7crrO5CDF.:10063:0:99999:7::: The `shadow` resource understands this format, allows you to search on the fields, and exposes the selected users' properties. diff --git a/docs-chef-io/content/inspec/resources/users.md b/docs-chef-io/content/inspec/resources/users.md index 7c7ef8cea..d4c567de1 100644 --- a/docs-chef-io/content/inspec/resources/users.md +++ b/docs-chef-io/content/inspec/resources/users.md @@ -142,7 +142,7 @@ The `badpasswordattempts` property tests the count of bad password attempts for where `0` is the count of bad passwords for a user. On Linux based operating systems it relies on `lastb` and for Windows it uses information stored for the user object. -These settings will be resetted to `0` depending on your operating system configuration. +These settings will reset to `0` depending on your operating system configuration. ## Examples diff --git a/docs-chef-io/content/inspec/resources/virtualization.md b/docs-chef-io/content/inspec/resources/virtualization.md index de1e6474d..08a76ac91 100644 --- a/docs-chef-io/content/inspec/resources/virtualization.md +++ b/docs-chef-io/content/inspec/resources/virtualization.md @@ -72,7 +72,7 @@ This helper returns, if any of the supported virtualization platforms was detect ### virtualization.physical_system? Helper -If no virtualization platform is detected, this will return `true`. For unsupported virtualization platforms this can result in false posititves. +If no virtualization platform is detected, this will return `true`. For unsupported virtualization platforms this can result in false positives. ### virtualization.system names diff --git a/docs-chef-io/content/inspec/resources/windows_task.md b/docs-chef-io/content/inspec/resources/windows_task.md index d32453151..0c9ea509d 100644 --- a/docs-chef-io/content/inspec/resources/windows_task.md +++ b/docs-chef-io/content/inspec/resources/windows_task.md @@ -70,9 +70,9 @@ The following examples show how to use this Chef InSpec resource. it { should exist } end -## Gathering Tasknames +## Gathering Task Names -Rather then use the GUI you can use the `schtasks.exe` to output a full list of tasks available on the system +Rather than use the GUI, you can use the `schtasks.exe` to output a full list of tasks available on the system `schtasks /query /FO list` diff --git a/docs-chef-io/content/inspec/resources/x509_certificate.md b/docs-chef-io/content/inspec/resources/x509_certificate.md index fc99048ab..eb2ef2457 100644 --- a/docs-chef-io/content/inspec/resources/x509_certificate.md +++ b/docs-chef-io/content/inspec/resources/x509_certificate.md @@ -29,13 +29,13 @@ This resource is available from InSpec version 1.18. An `x509_certificate` resource block declares a certificate `key file` to be tested. - describe x509_certificate('mycertificate.pem') do + describe x509_certificate('certificate.pem') do its('validity_in_days') { should be > 30 } end The `filepath` property can also be used. - describe x509_certificate(filepath: 'mycertificate.pem') do + describe x509_certificate(filepath: 'certificate.pem') do its('validity_in_days') { should be > 30 } end @@ -55,8 +55,8 @@ The `content` value is used if the `content` and `filepath` are specified. The `subject` (string) property accesses the individual subject elements. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do - its('subject.CN') { should eq "www.mywebsite.com" } + describe x509_certificate('/etc/pki/www.example.com.pem') do + its('subject.CN') { should eq "www.example.com" } end ### subject_dn @@ -65,15 +65,15 @@ The `subject_dn` (string) property returns the distinguished name of the subject For example, `/C=US/L=Seattle/O=Chef Software Inc/OU=Chefs/CN=Richard Nixon` - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do - its('subject_dn') { should match "CN=www.mywebsite.com" } + describe x509_certificate('/etc/pki/www.example.com.pem') do + its('subject_dn') { should match "CN=www.example.com" } end ### issuer.XX The `issuer` (string) property accesses the individual issuer elements. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('issuer.CN') { should eq "Acme Trust CA" } end @@ -83,7 +83,7 @@ During the certificate signing process, the `issuer_dn` (string) property is the For example, `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.org` - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('issuer_cn') { should match "CN=NAME CA" } end @@ -91,7 +91,7 @@ For example, `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.or The `public_key` (string) property returns a base64 encoded public key in PEM format. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('public_key') { should match "-----BEGIN PUBLIC KEY-----\nblah blah blah..." } end @@ -99,7 +99,7 @@ The `public_key` (string) property returns a base64 encoded public key in PEM fo The `key_length` (integer) property calculates the number of bits in the public key. If the length of bits in the public key increases, the public keys are secure. However, at the cost of speed and compatibility. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('key_length') { should be 2048 } end @@ -107,7 +107,7 @@ The `key_length` (integer) property calculates the number of bits in the public The `keylength` (integer) property is an alias of the `key_length` property. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('keylength') { should be 2048 } end @@ -115,7 +115,7 @@ The `keylength` (integer) property is an alias of the `key_length` property. The `signature_algorithm` (string) property describes the CA's hash function to sign the certificate. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('signature_algorithm') { should be 'sha256WithRSAEncryption' } end @@ -123,7 +123,7 @@ The `signature_algorithm` (string) property describes the CA's hash function to The `validity_in_days` (float) property is used to check the validity of the certificates. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('validity_in_days') { should be > 30 } end @@ -131,7 +131,7 @@ The `validity_in_days` (float) property is used to check the validity of the cer The `not_before` and `not_after` (time) properties expose the start and end dates of certificate validity. These dates are exposed as Ruby **Time** class and perform date calculations. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('not_before') { should be <= Time.utc.now } its('not_after') { should be >= Time.utc.now } end @@ -140,7 +140,7 @@ The `not_before` and `not_after` (time) properties expose the start and end date The `serial` (integer) property exposes the certificate's serial number. The CA sets the serial number during the signing process and should be unique within that CA. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('serial') { should eq 9623283588743302433 } end @@ -148,7 +148,7 @@ The `serial` (integer) property exposes the certificate's serial number. The CA The `version` (integer) property exposes the certificate version. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('version') { should eq 2 } end @@ -156,7 +156,7 @@ The `version` (integer) property exposes the certificate version. The `extensions` (hash) property is mainly used to determine the purpose of the certificate. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do # Check what extension categories we have its('extensions') { should include 'keyUsage' } its('extensions') { should include 'extendedKeyUsage' } @@ -179,16 +179,16 @@ The `extensions` (hash) property is mainly used to determine the purpose of the The `email` (string) property checks for the email address of the certificate. This is equivalent to invoking the property `subject.emailAddress`. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('email') { should_not be_empty } - its('email') { should eq 'admin@mywebsite.com' } + its('email') { should eq 'admin@example.com' } end ### subject_alt_names The `subject_alt_names` (string) property checks for the subject alternative names (additional host names) of the certificate. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do its('subject_alt_names') { should include 'DNS:example.com' } its('subject_alt_names') { should include 'DNS:www.example.com' } end @@ -203,7 +203,7 @@ The specific matchers of this resource are: `be_valid`, `be_certificate` and `ha The `be_valid` matcher tests if the specified certificate is valid. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do it { should be_valid } end @@ -211,7 +211,7 @@ The `be_valid` matcher tests if the specified certificate is valid. The `be_certificate` matcher tests if the specified content or file is a certificate. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do it { should be_certificate } end @@ -219,7 +219,7 @@ The `be_certificate` matcher tests if the specified content or file is a certifi The `have_purpose` matcher tests if the certificate meets the specified purpose. - describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do + describe x509_certificate('/etc/pki/www.example.com.pem') do it { should have_purpose('SSL client CA : Yes') } it { should have_purpose('SSL server CA : Yes') } end diff --git a/docs-chef-io/content/inspec/shell.md b/docs-chef-io/content/inspec/shell.md index 45108f447..b1e947b63 100644 --- a/docs-chef-io/content/inspec/shell.md +++ b/docs-chef-io/content/inspec/shell.md @@ -112,12 +112,12 @@ $ inspec shell Welcome to the interactive InSpec Shell To find out how to use it, type: help -inspec> file('/Users/myuser').directory? +inspec> file('/Users/username').directory? => true inspec> os_env('HOME') => Environment variable HOME inspec> os_env('HOME').content -=> /Users/myuser +=> /Users/username inspec> exit ``` @@ -141,10 +141,10 @@ replaced with the redefinition and the control is re-run. ```bash inspec> control 'my_control' do inspec> describe os_env('HOME') do -inspec> its('content') { should eq '/Users/myuser' } +inspec> its('content') { should eq '/Users/username' } inspec> end inspec> end - ✔ my_control: Environment variable HOME content should eq "/Users/myuser" + ✔ my_control: Environment variable HOME content should eq "/Users/username" Summary: 1 successful, 0 failures, 0 skipped ``` @@ -173,10 +173,10 @@ If you wish to run a single Chef InSpec command and fetch its results, you may use the `-c` flag. This is similar to using `bash -c`. ```bash -$ inspec shell -c 'describe file("/Users/myuser") do it { should exist } end' +$ inspec shell -c 'describe file("/Users/username") do it { should exist } end' Target: local:// - ✔ File /Users/myuser should exist + ✔ File /Users/username should exist Summary: 1 successful, 0 failures, 0 skipped ``` From a3beccfa4349297802cff69a3bac318707471421 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 21 Jul 2022 05:50:37 +0000 Subject: [PATCH 54/93] Bump version to 5.18.16 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd41e0c2f..fb1a245db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.15](https://github.com/inspec/inspec/tree/v5.18.15) (2022-07-13) + +## [v5.18.16](https://github.com/inspec/inspec/tree/v5.18.16) (2022-07-21) #### Merged Pull Requests -- Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) +- Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) ### Changes since 5.18.14 release #### Merged Pull Requests +- Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) - Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index e140359bb..569674dc0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.15 \ No newline at end of file +5.18.16 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 74590666d..09b08a4d1 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.15".freeze + VERSION = "5.18.16".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 4aa1e89fe..c786aa96a 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.15".freeze + VERSION = "5.18.16".freeze end From 28c505b1a7f091abe1ee51b7cd188853edab48bb Mon Sep 17 00:00:00 2001 From: Vasu1105 Date: Wed, 27 Jul 2022 18:03:53 +0530 Subject: [PATCH 55/93] CFINSPEC-400 Fix for verify pipeline failure Signed-off-by: Vasu1105 --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index a1bb0b915..de753e71b 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ end group :test do gem "chefstyle", "~> 2.0.3" gem "concurrent-ruby", "~> 1.0" - gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows + 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. gem "json_schemer", ">= 0.2.1", "< 0.2.19" gem "m" gem "minitest-sprint", "~> 1.0" From bb802f8dc9ae25ecc24ab1e5ca3af64750673072 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Wed, 27 Jul 2022 14:00:17 +0000 Subject: [PATCH 56/93] Bump version to 5.18.17 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1a245db..fd88dbe53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.16](https://github.com/inspec/inspec/tree/v5.18.16) (2022-07-21) + +## [v5.18.17](https://github.com/inspec/inspec/tree/v5.18.17) (2022-07-27) #### Merged Pull Requests -- Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) +- CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) ### Changes since 5.18.14 release #### Merged Pull Requests +- CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) - Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) - Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index 569674dc0..6d8f9c9ed 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.16 \ No newline at end of file +5.18.17 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index 09b08a4d1..e0a737545 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.16".freeze + VERSION = "5.18.17".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index c786aa96a..7f8b4aeed 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.16".freeze + VERSION = "5.18.17".freeze end From 4b7543d109cdafa8e16493d73cce667f6dfb5563 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 15 Jun 2022 17:52:34 +0530 Subject: [PATCH 57/93] Added enhanced_outcomes option for exec and shell Signed-off-by: Nikita Mathur --- lib/inspec/base_cli.rb | 2 ++ lib/inspec/cli.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/inspec/base_cli.rb b/lib/inspec/base_cli.rb index f876af6f8..89ce04c7f 100644 --- a/lib/inspec/base_cli.rb +++ b/lib/inspec/base_cli.rb @@ -205,6 +205,8 @@ module Inspec long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error." option :reporter_include_source, type: :boolean, default: false, desc: "Include full source code of controls in the CLI report" + option :enhanced_outcomes, type: :boolean, + desc: "Show enhanced outcomes in output" end def self.help(*args) diff --git a/lib/inspec/cli.rb b/lib/inspec/cli.rb index 052d837bb..47f91ec78 100644 --- a/lib/inspec/cli.rb +++ b/lib/inspec/cli.rb @@ -415,6 +415,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI desc: "Load one or more input files, a YAML file with values for the shell to use" option :input, type: :array, banner: "name1=value1 name2=value2", 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." + option :enhanced_outcomes, type: :boolean, + desc: "Show enhanced outcomes in output" def shell_func o = config deprecate_target_id(config) From 29cd28f6acb1acc3fa56ff9cf5bf7137d5f6b5b1 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 17 Jun 2022 17:28:11 +0530 Subject: [PATCH 58/93] Added plumbing logic to integrate enhanced outcomes in run_data Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 16 ++++++++++++++-- lib/inspec/runner_rspec.rb | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index baf29be9c..172b473d1 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -6,7 +6,7 @@ module Inspec::Formatters class Base < RSpec::Core::Formatters::BaseFormatter RSpec::Core::Formatters.register self, :close, :dump_summary, :stop - attr_accessor :backend, :run_data + attr_accessor :backend, :run_data, :enhanced_outcomes def initialize(output) super(output) @@ -17,6 +17,7 @@ module Inspec::Formatters @backend = nil @all_controls_count = nil @control_checks_count_map = {} + @enhanced_outcomes = nil end # RSpec Override: #dump_summary @@ -50,7 +51,6 @@ module Inspec::Formatters else hash[:message] = exception_message(e) end - next if e.is_a? RSpec::Expectations::ExpectationNotMetError hash[:exception] = e.class.name @@ -68,6 +68,8 @@ module Inspec::Formatters # flesh out the profiles key with additional profile information run_data[:profiles] = profiles_info + add_enhanced_outcomes_to_controls if enhanced_outcomes + # add the platform information for this particular target run_data[:platform] = { name: platform(:name), @@ -110,6 +112,16 @@ module Inspec::Formatters private + def add_enhanced_outcomes_to_controls + all_unique_controls.each do |control| + control[:status] = determine_control_enhanced_outcome(control) + end + end + + def determine_control_enhanced_outcome(control) + # logic to find enhanced_outcome status + end + def all_unique_controls unique_controls = Set.new run_data[:profiles].each do |profile| diff --git a/lib/inspec/runner_rspec.rb b/lib/inspec/runner_rspec.rb index e14d53476..fde0c975b 100644 --- a/lib/inspec/runner_rspec.rb +++ b/lib/inspec/runner_rspec.rb @@ -196,6 +196,7 @@ module Inspec def configure_output RSpec.configuration.output_stream = $stdout @formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base) + @formatter.enhanced_outcomes = @conf.final_options["enhanced_outcomes"] RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress] set_optional_formatters RSpec.configuration.color = @conf["color"] From 390d883f9977739b8b8a3253a80275869d8dd57e Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 20 Jun 2022 17:40:58 +0530 Subject: [PATCH 59/93] Test added for enhanced_outcomes N/A and N/R Signed-off-by: Nikita Mathur --- .../skip-control/controls/skip-control.rb | 1 + test/functional/helper.rb | 1 + test/functional/inspec_exec_test.rb | 18 ++++++ test/functional/inspec_shell_test.rb | 57 ++++++++++++++++++- 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb b/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb index 8aaf8f945..045fcf0a8 100644 --- a/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb +++ b/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb @@ -6,6 +6,7 @@ control "control-start" do end control "control-skip-no-message" do + impact 0.0 desc "This control should get skipped" only_if { false } describe "a string" do diff --git a/test/functional/helper.rb b/test/functional/helper.rb index 81f6aa009..2931ebf8a 100644 --- a/test/functional/helper.rb +++ b/test/functional/helper.rb @@ -186,6 +186,7 @@ module FunctionalHelper prefix += assemble_env_prefix(opts[:env]) command_line += " --reporter json " if opts[:json] && command_line =~ /\bexec\b/ command_line += " --no-create-lockfile " if (!opts[:lock]) && command_line =~ /\bexec\b/ + command_line += " --enhanced_outcomes " if (opts[:enhanced_outcomes]) && command_line =~ /\bexec\b/ run_result = nil if opts[:tmpdir] diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 9c4be8756..121fb1121 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1331,4 +1331,22 @@ EOT end end end + + describe "when running profile with enhanced_outcomes option" do + let(:run_result) { run_inspec_process("exec #{profile} --no-create-lockfile", enhanced_outcomes: true) } + let(:profile) { "#{profile_path}/only_if/skip-control" } + it "should evaluate all test controls correctly" do + _(run_result.stderr).must_be_empty + end + + it "should show enhanced_outcomes for skipped tests in controls" do + _(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 6 controls skipped\n" + _(stdout).must_include "Not Reviewed" + end + + it "should show enhanced_outcomes for controls with impact 0" do + _(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 6 controls skipped\n" + _(stdout).must_include "Not Applicable" + end + end end diff --git a/test/functional/inspec_shell_test.rb b/test/functional/inspec_shell_test.rb index 07ff1b9e3..641e30806 100644 --- a/test/functional/inspec_shell_test.rb +++ b/test/functional/inspec_shell_test.rb @@ -7,8 +7,9 @@ describe "inspec shell tests" do parallelize_me! describe "cmd" do - def assert_shell_c(code, exit_status, json = false, stderr = "") + def assert_shell_c(code, exit_status, json = false, stderr = "", enhanced_outcomes = false) json_suffix = " --reporter 'json'" if json + json_suffix = " --enhanced_outcomes" if enhanced_outcomes command = "shell -c '#{code.tr("'", "\\'")}'#{json_suffix}" # On darwin this value is: # shell -c 'describe file(\"/Users/nickschwaderer/Documents/inspec/inspec/test/functional/inspec_shell_test.rb\") do it { should exist } end' --reporter 'json'" @@ -219,6 +220,33 @@ describe "inspec shell tests" do _(out.stdout).must_include "1 successful" _(out.stdout).must_include "0 failures" end + + it "runs controls having skipped tests with enhanced_outcomes option" do + skip_windows! # Breakage confirmed + out = assert_shell_c("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 101 , false, "", true) + _(out.stdout).must_include "Not Reviewed" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "1 skipped" + end + + it "runs zero impact controls with enhanced_outcomes option" do + skip_windows! # Breakage confirmed + out = assert_shell_c("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, false, "", true) + _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "1 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "0 skipped" + end + + it "runs zero impact controls with skipped test and enhanced_outcomes option" do + skip_windows! # Breakage confirmed + out = assert_shell_c("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 101, false, "", true) + _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "1 skipped" + end end # Pry does not support STDIN from windows currently. Skipping these for now. @@ -235,8 +263,9 @@ describe "inspec shell tests" do out.stderr.gsub(/\e\[(\d+)(;\d+)*m/, "") # strip ANSI color codes end - def do_shell(code, exit_status = 0, stderr = "") + def do_shell(code, exit_status = 0, stderr = "", enhanced_outcomes = false) cmd = "echo '#{code.tr("'", "\\'")}' | #{exec_inspec} shell" + cmd += " --enhanced_outcomes" if enhanced_outcomes self.out = CMD.run_command(cmd) assert_exit_code exit_status, out @@ -349,6 +378,30 @@ describe "inspec shell tests" do _(out.stdout).must_include "1 successful" _(out.stdout).must_include "1 failure" end + + it "runs controls having skipped tests with enhanced_outcomes option" do + out = do_shell("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 101 , "", true) + _(out.stdout).must_include "Not Reviewed" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "1 skipped" + end + + it "runs zero impact controls with enhanced_outcomes option" do + out = do_shell("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, "", true) + _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "1 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "0 skipped" + end + + it "runs zero impact controls with skipped test and enhanced_outcomes option" do + out = do_shell("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 101, "", true) + _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "0 failures" + _(out.stdout).must_include "1 skipped" + end end end end From 8c2d78161afd00738165c9699653b2375ca5db20 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 20 Jun 2022 18:22:05 +0530 Subject: [PATCH 60/93] N/A and N/R logic integration with base formatter run_data Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 18 +++++++++++++++++- .../skip-control/controls/skip-control.rb | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index 172b473d1..f5ab11b33 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -119,7 +119,23 @@ module Inspec::Formatters end def determine_control_enhanced_outcome(control) - # logic to find enhanced_outcome status + if control_has_error(control) + return { name: "Error", abbrev: "ERR" } + elsif control[:impact].to_f == 0.0 + return { name: "Not Applicable", abbrev: "N/A" } + elsif control_has_all_tests_skipped(control) + return { name: "Not Reviewed", abbrev: "N/R" } + end + + {} + end + + def control_has_all_tests_skipped(control) + control[:results].all? { |r| r[:status] == "skipped" } + end + + def control_has_error(control) + # logic to determine error in control end def all_unique_controls diff --git a/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb b/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb index 045fcf0a8..e9618ab93 100644 --- a/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb +++ b/test/fixtures/profiles/only_if/skip-control/controls/skip-control.rb @@ -1,4 +1,5 @@ control "control-start" do + impact 0.1 desc "This control should not get skipped" describe "a string" do it { should cmp "a string" } @@ -15,6 +16,7 @@ control "control-skip-no-message" do end control "control-skip-with-message" do + impact 0.1 desc "This control should get skipped" only_if("here is a message") { false } describe "a string" do @@ -23,6 +25,7 @@ control "control-skip-with-message" do end control "control-skip-test-body" do + impact 0.1 desc "This control should demo that the test body does not get evaluated" only_if { false } describe "infinity" do @@ -31,6 +34,7 @@ control "control-skip-test-body" do end control "control-skip-test-outer-error" do + impact 0.5 desc "This control should demo that following test resources do not get evaluated" only_if { false } describe 1/0 do # does not error! @@ -39,6 +43,7 @@ control "control-skip-test-outer-error" do end control "control-skip-test-outer-resource-test-first" do + impact 0.5 desc "This control should demo that preceding test resources DO NOT get evaluated" describe command("echo toldyaso") do # does exec its("stdout") { should include "toldya" } @@ -47,6 +52,7 @@ control "control-skip-test-outer-resource-test-first" do end control "multi-skip" do + impact 0.1 desc "This control should get skipped" only_if("here is the intended message") { false } only_if("here is a different message") { false } From e7aa37dc246f25d4253625b37b2372fcb243a415 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Tue, 21 Jun 2022 17:26:22 +0530 Subject: [PATCH 61/93] Added logic for error in run_data Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 4 ++-- test/functional/helper.rb | 2 +- test/functional/inspec_exec_test.rb | 18 ++++++++++++++---- test/functional/inspec_shell_test.rb | 12 ++++++++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index f5ab11b33..a507a558b 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -131,11 +131,11 @@ module Inspec::Formatters end def control_has_all_tests_skipped(control) - control[:results].all? { |r| r[:status] == "skipped" } + control[:results] && control[:results].all? { |r| r[:status] == "skipped" } end def control_has_error(control) - # logic to determine error in control + control[:results] && (control[:results].any? { |r| !r[:exception].nil? && !r[:backtrace].nil? }) end def all_unique_controls diff --git a/test/functional/helper.rb b/test/functional/helper.rb index 2931ebf8a..213de3cd3 100644 --- a/test/functional/helper.rb +++ b/test/functional/helper.rb @@ -186,7 +186,7 @@ module FunctionalHelper prefix += assemble_env_prefix(opts[:env]) command_line += " --reporter json " if opts[:json] && command_line =~ /\bexec\b/ command_line += " --no-create-lockfile " if (!opts[:lock]) && command_line =~ /\bexec\b/ - command_line += " --enhanced_outcomes " if (opts[:enhanced_outcomes]) && command_line =~ /\bexec\b/ + command_line += " --enhanced_outcomes " if opts[:enhanced_outcomes] && command_line =~ /\bexec\b/ run_result = nil if opts[:tmpdir] diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 121fb1121..26e433ae1 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1340,13 +1340,23 @@ EOT end it "should show enhanced_outcomes for skipped tests in controls" do - _(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 6 controls skipped\n" - _(stdout).must_include "Not Reviewed" + _(run_result.stdout).must_include "6 controls skipped" + _(run_result.stdout).must_include "Not Reviewed" end it "should show enhanced_outcomes for controls with impact 0" do - _(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 6 controls skipped\n" - _(stdout).must_include "Not Applicable" + _(run_result.stdout).must_include "6 controls skipped" + _(run_result.stdout).must_include "Not Applicable" + end + end + + describe "when running profile with errors and enhanced_outcomes option" do + let(:run_result) { run_inspec_process("exec #{profile} --no-create-lockfile", enhanced_outcomes: true) } + let(:profile) { "#{profile_path}/exception-in-control" } + + it "should show enhanced_outcomes for controls with errors" do + _(run_result.stdout).must_include "4 control failures" + _(run_result.stdout).must_include "[Error]" end end end diff --git a/test/functional/inspec_shell_test.rb b/test/functional/inspec_shell_test.rb index 641e30806..1f021c26f 100644 --- a/test/functional/inspec_shell_test.rb +++ b/test/functional/inspec_shell_test.rb @@ -380,7 +380,7 @@ describe "inspec shell tests" do end it "runs controls having skipped tests with enhanced_outcomes option" do - out = do_shell("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 101 , "", true) + out = do_shell("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 0 , "", true) _(out.stdout).must_include "Not Reviewed" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" @@ -396,12 +396,20 @@ describe "inspec shell tests" do end it "runs zero impact controls with skipped test and enhanced_outcomes option" do - out = do_shell("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 101, "", true) + out = do_shell("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, "", true) _(out.stdout).must_include "Not Applicable" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "1 skipped" end + + it "runs control with error and enhanced_outcomes option" do + out = do_shell("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { must_bot exist } end end", 0, "", true) + _(out.stdout).must_include "Error" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "1 failure" + _(out.stdout).must_include "0 skipped" + end end end end From f56f5ec844428dd937278e6f88bf7898b50c5a1b Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 22 Jun 2022 16:41:57 +0530 Subject: [PATCH 62/93] enhanced outcomes logic in streaming reporter base class Signed-off-by: Nikita Mathur --- .../v2/plugin_types/streaming_reporter.rb | 41 +++++++++++++++++++ lib/inspec/runner.rb | 14 +++++-- .../streaming_reporter.rb | 6 ++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb index 5def981c2..0cd302009 100644 --- a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +++ b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb @@ -11,6 +11,7 @@ module Inspec::Plugin::V2::PluginType @running_controls_list = [] @control_checks_count_map = {} @controls_count = nil + @notifications = [] end private @@ -49,5 +50,45 @@ module Inspec::Plugin::V2::PluginType @control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map end end + + def enhanced_outcomes + @enhanced_outcomes ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.enhanced_outcomes + end + + def add_enhanced_outcomes + enhanced_outcomes = {} + if control_has_error(@notifications) + enhanced_outcomes = { name: "Error", abbrev: "ERR" } + elsif control_has_impact_zero(@notifications) + enhanced_outcomes = { name: "Not Applicable", abbrev: "N/A" } + elsif control_has_all_tests_skipped(@notifications) + enhanced_outcomes = { name: "Not Reviewed", abbrev: "N/R" } + end + @notifications = [] + enhanced_outcomes + end + + def control_has_error(notifications) + notifications.any? do |notification_data| + notification, _status = notification_data + !notification.example.exception.nil? && !(notification.example.exception.is_a? RSpec::Expectations::ExpectationNotMetError) && !notification.example.exception.backtrace.nil? + end + end + + def control_has_all_tests_skipped(notifications) + notifications.all? do |notification_data| + _notification, status = notification_data + status == "skipped" + end + end + + def control_has_impact_zero(notifications) + notification_data = notifications.first + notification_data && notification_data.first.example.metadata[:impact].to_f == 0.0 + end + + def collect_notifications(notification, status) + @notifications.push([notification, status]) + end end end diff --git a/lib/inspec/runner.rb b/lib/inspec/runner.rb index 5faaa4325..43c792461 100644 --- a/lib/inspec/runner.rb +++ b/lib/inspec/runner.rb @@ -133,12 +133,20 @@ module Inspec all_controls.each do |rule| unless rule.nil? register_rule(rule) - checks = ::Inspec::Rule.prepare_checks(rule) - unless checks.empty? + total_checks = 0 + control_describe_checks = ::Inspec::Rule.prepare_checks(rule) + + examples = control_describe_checks.flat_map do |m, a, b| + get_check_example(m, a, b) + end.compact + + examples.map { |example| total_checks += example.examples.count } + + unless control_describe_checks.empty? # controls with empty tests are avoided # checks represent tests within control controls_count += 1 - control_checks_count_map[rule.to_s] = checks.count + control_checks_count_map[rule.to_s] = control_checks_count_map[rule.to_s].to_i + total_checks end end end diff --git a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb index 92028d43e..3e6f61c4c 100644 --- a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +++ b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb @@ -73,7 +73,11 @@ module InspecPlugins::StreamingReporterProgressBar full_description = notification.example.metadata[:full_description] control_impact = notification.example.metadata[:impact] set_status_mapping(control_id, status) - show_progress(control_id, title, full_description, control_impact) if control_ended?(control_id) + collect_notifications(notification, status) + control_ended = control_ended?(control_id) + if control_ended + show_progress(control_id, title, full_description, control_impact) + end end def show_progress(control_id, title, full_description, control_impact) From 6d4729c437ba1b7cc8c66dcd3e82f200d04582e4 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 22 Jun 2022 17:37:32 +0530 Subject: [PATCH 63/93] Progress bar streaming reporter fix for unique control counts Signed-off-by: Nikita Mathur --- lib/inspec/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inspec/runner.rb b/lib/inspec/runner.rb index 43c792461..50a43446d 100644 --- a/lib/inspec/runner.rb +++ b/lib/inspec/runner.rb @@ -145,7 +145,7 @@ module Inspec unless control_describe_checks.empty? # controls with empty tests are avoided # checks represent tests within control - controls_count += 1 + controls_count += 1 if control_checks_count_map[rule.to_s].nil? control_checks_count_map[rule.to_s] = control_checks_count_map[rule.to_s].to_i + total_checks end end From 4e83f395c807da47fea1981844c39c90a0e764d6 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 22 Jun 2022 18:12:22 +0530 Subject: [PATCH 64/93] enhanced outcome fix in streaming reporter - collected test based on control_id Signed-off-by: Nikita Mathur --- .../v2/plugin_types/streaming_reporter.rb | 22 +++++++++++-------- .../streaming_reporter.rb | 3 ++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb index 0cd302009..ed94063fd 100644 --- a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +++ b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb @@ -11,7 +11,7 @@ module Inspec::Plugin::V2::PluginType @running_controls_list = [] @control_checks_count_map = {} @controls_count = nil - @notifications = [] + @notifications = {} end private @@ -55,16 +55,15 @@ module Inspec::Plugin::V2::PluginType @enhanced_outcomes ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.enhanced_outcomes end - def add_enhanced_outcomes + def add_enhanced_outcomes(control_id) enhanced_outcomes = {} - if control_has_error(@notifications) + if control_has_error(@notifications[control_id]) enhanced_outcomes = { name: "Error", abbrev: "ERR" } - elsif control_has_impact_zero(@notifications) + elsif control_has_impact_zero(@notifications[control_id]) enhanced_outcomes = { name: "Not Applicable", abbrev: "N/A" } - elsif control_has_all_tests_skipped(@notifications) + elsif control_has_all_tests_skipped(@notifications[control_id]) enhanced_outcomes = { name: "Not Reviewed", abbrev: "N/R" } end - @notifications = [] enhanced_outcomes end @@ -84,11 +83,16 @@ module Inspec::Plugin::V2::PluginType def control_has_impact_zero(notifications) notification_data = notifications.first - notification_data && notification_data.first.example.metadata[:impact].to_f == 0.0 + notification_impact = notification_data.first.example.metadata[:impact] + notification_data && !notification_impact.nil? && notification_impact.to_f == 0.0 end - def collect_notifications(notification, status) - @notifications.push([notification, status]) + def collect_notifications(notification, control_id, status) + if @notifications[control_id].nil? + @notifications[control_id] = [[notification, status]] + else + @notifications[control_id].push([notification, status]) + end end end end diff --git a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb index 3e6f61c4c..7eed0ac67 100644 --- a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +++ b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb @@ -73,9 +73,10 @@ module InspecPlugins::StreamingReporterProgressBar full_description = notification.example.metadata[:full_description] control_impact = notification.example.metadata[:impact] set_status_mapping(control_id, status) - collect_notifications(notification, status) + collect_notifications(notification, control_id, status) control_ended = control_ended?(control_id) if control_ended + add_enhanced_outcomes(control_id) if enhanced_outcomes show_progress(control_id, title, full_description, control_impact) end end From 9d531b68deb311916a48d07e08b6bcd3bb645d01 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 24 Jun 2022 16:04:10 +0530 Subject: [PATCH 65/93] Added failure and passed conditions in enhanced_outcomes Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 12 +++++++----- .../v2/plugin_types/streaming_reporter.rb | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index a507a558b..f0a1b3876 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -120,14 +120,16 @@ module Inspec::Formatters def determine_control_enhanced_outcome(control) if control_has_error(control) - return { name: "Error", abbrev: "ERR" } + { name: "Error", abbrev: "ERR" } elsif control[:impact].to_f == 0.0 - return { name: "Not Applicable", abbrev: "N/A" } + { name: "Not Applicable", abbrev: "N/A" } elsif control_has_all_tests_skipped(control) - return { name: "Not Reviewed", abbrev: "N/R" } + { name: "Not Reviewed", abbrev: "N/R" } + elsif control[:results].any? { |r| r[:status] == "failed" } + { name: "Failed", abbrev: "fail"} + else + { name: "Passed", abbrev: "pass"} end - - {} end def control_has_all_tests_skipped(control) diff --git a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb index ed94063fd..7de4b83bb 100644 --- a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +++ b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb @@ -56,15 +56,17 @@ module Inspec::Plugin::V2::PluginType end def add_enhanced_outcomes(control_id) - enhanced_outcomes = {} if control_has_error(@notifications[control_id]) - enhanced_outcomes = { name: "Error", abbrev: "ERR" } + { name: "Error", abbrev: "ERR" } elsif control_has_impact_zero(@notifications[control_id]) - enhanced_outcomes = { name: "Not Applicable", abbrev: "N/A" } + { name: "Not Applicable", abbrev: "N/A" } elsif control_has_all_tests_skipped(@notifications[control_id]) - enhanced_outcomes = { name: "Not Reviewed", abbrev: "N/R" } + { name: "Not Reviewed", abbrev: "N/R" } + elsif control_has_any_tests_failed(@notifications[control_id]) + { name: "Failed", abbrev: "fail" } + else + { name: "Passed", abbrev: "pass" } end - enhanced_outcomes end def control_has_error(notifications) @@ -81,6 +83,13 @@ module Inspec::Plugin::V2::PluginType end end + def control_has_any_tests_failed(notifications) + notifications.any? do |notification_data| + _notification, status = notification_data + status == "failed" + end + end + def control_has_impact_zero(notifications) notification_data = notifications.first notification_impact = notification_data.first.example.metadata[:impact] From 2ebd8905338a3d113cbd3eed72f4ad254e600513 Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Mon, 27 Jun 2022 16:29:34 -0400 Subject: [PATCH 66/93] Linting Signed-off-by: Clinton Wolfe --- lib/inspec/formatters/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index f0a1b3876..d867b492d 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -126,9 +126,9 @@ module Inspec::Formatters elsif control_has_all_tests_skipped(control) { name: "Not Reviewed", abbrev: "N/R" } elsif control[:results].any? { |r| r[:status] == "failed" } - { name: "Failed", abbrev: "fail"} + { name: "Failed", abbrev: "fail" } else - { name: "Passed", abbrev: "pass"} + { name: "Passed", abbrev: "pass" } end end From 2b9f4d4bbf9a854f45d7ec17ac5f88175a509ee5 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 27 Jun 2022 15:36:12 +0530 Subject: [PATCH 67/93] Enhanced outcomes changes in cli reporter Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 4 +- lib/inspec/reporters.rb | 3 +- lib/inspec/reporters/base.rb | 1 + lib/inspec/reporters/cli.rb | 97 +++++++++++++++++++++++++++- lib/inspec/runner.rb | 2 +- test/functional/inspec_exec_test.rb | 12 ++-- test/functional/inspec_shell_test.rb | 23 +++++-- 7 files changed, 122 insertions(+), 20 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index d867b492d..44afe858a 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -125,8 +125,8 @@ module Inspec::Formatters { name: "Not Applicable", abbrev: "N/A" } elsif control_has_all_tests_skipped(control) { name: "Not Reviewed", abbrev: "N/R" } - elsif control[:results].any? { |r| r[:status] == "failed" } - { name: "Failed", abbrev: "fail" } + elsif control[:results] && control[:results].any? { |r| r[:status] == "failed" } + { name: "Failed", abbrev: "fail"} else { name: "Passed", abbrev: "pass" } end diff --git a/lib/inspec/reporters.rb b/lib/inspec/reporters.rb index daf8ad436..f5cb0aebd 100644 --- a/lib/inspec/reporters.rb +++ b/lib/inspec/reporters.rb @@ -7,7 +7,7 @@ require "inspec/reporters/yaml" module Inspec::Reporters # rubocop:disable Metrics/CyclomaticComplexity - def self.render(reporter, run_data) + def self.render(reporter, run_data, enhanced_outcomes = false) name, config = reporter.dup config[:run_data] = run_data case name @@ -29,6 +29,7 @@ module Inspec::Reporters activator.activate! reporter = activator.implementation_class.new(config) end + reporter.enhanced_outcomes = enhanced_outcomes # optional send_report method on reporter return reporter.send_report if defined?(reporter.send_report) diff --git a/lib/inspec/reporters/base.rb b/lib/inspec/reporters/base.rb index 9060ea4df..61b83d79a 100644 --- a/lib/inspec/reporters/base.rb +++ b/lib/inspec/reporters/base.rb @@ -5,6 +5,7 @@ module Inspec::Reporters include Inspec::Utils::RunDataFilters attr_reader :run_data + attr_accessor :enhanced_outcomes def initialize(config) @config = config diff --git a/lib/inspec/reporters/cli.rb b/lib/inspec/reporters/cli.rb index 14263cdf6..b85fa2cc8 100644 --- a/lib/inspec/reporters/cli.rb +++ b/lib/inspec/reporters/cli.rb @@ -9,6 +9,9 @@ module Inspec::Reporters "passed" => "\033[0;1;32m", "skipped" => "\033[0;37m", "reset" => "\033[0m", + "error" => "\033[34m", + "not_applicable" => "\033[36m", + "not_reviewed" => "\033[33m", }.freeze # Most currently available Windows terminals have poor support @@ -18,6 +21,9 @@ module Inspec::Reporters "skipped" => "[SKIP]", "passed" => "[PASS]", "unknown" => "[UNKN]", + "error" => "[ERR]", + "not_applicable" => "[N/A]", + "not_reviewed" => "[N/R]", }.freeze else # Extended colors for everyone else @@ -26,6 +32,9 @@ module Inspec::Reporters "passed" => "\033[38;5;41m", "skipped" => "\033[38;5;247m", "reset" => "\033[0m", + "error" => "\033[0;38;5;21m", + "not_applicable" => "\033[0;38;5;117m", + "not_reviewed" => "\033[0;38;5;214m", }.freeze # Groovy UTF-8 characters for everyone else... @@ -35,6 +44,9 @@ module Inspec::Reporters "skipped" => "↺", "passed" => "✔", "unknown" => "?", + "error" => "ERR", + "not_applicable" => "N/A", + "not_reviewed" => "N/R", }.freeze end @@ -63,7 +75,11 @@ module Inspec::Reporters end output("") - print_profile_summary + if enhanced_outcomes + print_control_outcomes_summary + else + print_profile_summary + end print_tests_summary end @@ -88,6 +104,7 @@ module Inspec::Reporters def print_standard_control_results(profile) standard_controls_from_profile(profile).each do |control_from_profile| control = Control.new(control_from_profile) + control.enhanced_outcomes = enhanced_outcomes next if control.results.nil? output(format_control_header(control)) @@ -122,7 +139,7 @@ module Inspec::Reporters end def format_control_header(control) - impact = control.impact_string + impact = enhanced_outcomes ? control.impact_string_for_enhanced_outcomes : control.impact_string format_message( color: impact, indicator: impact, @@ -292,6 +309,68 @@ module Inspec::Reporters } end + def control_outcomes_summary + failed = 0 + passed = 0 + error = 0 + not_reviewed = 0 + not_applicable = 0 + + all_unique_controls.each do |control| + next if control[:status].empty? + + if control[:status][:name] == "Failed" + failed += 1 + elsif control[:status][:name] == "Error" + error += 1 + elsif control[:status][:name] == "Not Reviewed" + not_reviewed += 1 + elsif control[:status][:name] == "Not Applicable" + not_applicable += 1 + else + passed += 1 + end + end + + total = failed + passed + error + not_reviewed + not_applicable + + { + "total" => total, + "failed" => failed, + "passed" => passed, + "error" => error, + "not_reviewed" => not_reviewed, + "not_applicable" => not_applicable, + } + end + + def print_control_outcomes_summary + summary = control_outcomes_summary + return unless summary["total"] > 0 + + success_str = summary["passed"] == 1 ? "1 successful control" : "#{summary["passed"]} successful controls" + failed_str = summary["failed"] == 1 ? "1 control failure" : "#{summary["failed"]} control failures" + error_str = summary["error"] == 1 ? "1 control has error" : "#{summary["error"]} controls have error" + not_rev_str = summary["not_reviewed"] == 1 ? "1 control not reviewed" : "#{summary["not_reviewed"]} controls not reviewed" + not_app_str = summary["not_applicable"] == 1 ? "1 control not applicable" : "#{summary["not_applicable"]} controls not applicable" + + success_color = summary["passed"] > 0 ? "passed" : "no_color" + failed_color = summary["failed"] > 0 ? "failed" : "no_color" + error_color = summary["error"] > 0 ? "error" : "no_color" + not_rev_color = summary["not_reviewed"] > 0 ? "not_reviewed" : "no_color" + not_app_color = summary["not_applicable"] > 0 ? "not_applicable" : "no_color" + + s = format( + "Profile Summary: %s, %s, %s, %s, %s", + format_with_color(success_color, success_str), + format_with_color(failed_color, failed_str), + format_with_color(not_rev_color, not_rev_str), + format_with_color(not_app_color, not_app_str), + format_with_color(error_color, error_str), + ) + output(s) if summary["total"] > 0 + end + def print_profile_summary summary = profile_summary return unless summary["total"] > 0 @@ -350,6 +429,7 @@ module Inspec::Reporters class Control attr_reader :data + attr_accessor :enhanced_outcomes def initialize(control_hash) @data = control_hash @@ -379,6 +459,10 @@ module Inspec::Reporters id.start_with?("(generated from ") end + def status + data[:status] + end + def title_for_report # if this is an anonymous control, just grab the resource title from any result entry return results.first[:resource_title] if anonymous? @@ -392,10 +476,17 @@ module Inspec::Reporters # append a failure summary if appropriate. title_for_report += " (#{failure_count} failed)" if failure_count > 0 title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0 - title_for_report end + def impact_string_for_enhanced_outcomes + if impact.nil? + "unknown" + else + status[:name].downcase.gsub(" ", "_") + end + end + def impact_string if anonymous? nil diff --git a/lib/inspec/runner.rb b/lib/inspec/runner.rb index 50a43446d..292208f0f 100644 --- a/lib/inspec/runner.rb +++ b/lib/inspec/runner.rb @@ -166,7 +166,7 @@ module Inspec return if @conf["reporter"].nil? @conf["reporter"].each do |reporter| - result = Inspec::Reporters.render(reporter, run_data) + result = Inspec::Reporters.render(reporter, run_data, @conf["enhanced_outcomes"]) raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false end end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 26e433ae1..1e0f4d212 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1340,13 +1340,13 @@ EOT end it "should show enhanced_outcomes for skipped tests in controls" do - _(run_result.stdout).must_include "6 controls skipped" - _(run_result.stdout).must_include "Not Reviewed" + _(run_result.stdout).must_include "6 skipped" + _(run_result.stdout).must_include "5 controls not reviewed" end it "should show enhanced_outcomes for controls with impact 0" do - _(run_result.stdout).must_include "6 controls skipped" - _(run_result.stdout).must_include "Not Applicable" + _(run_result.stdout).must_include "6 skipped" + _(run_result.stdout).must_include "1 control not applicable" end end @@ -1355,8 +1355,8 @@ EOT let(:profile) { "#{profile_path}/exception-in-control" } it "should show enhanced_outcomes for controls with errors" do - _(run_result.stdout).must_include "4 control failures" - _(run_result.stdout).must_include "[Error]" + _(run_result.stdout).must_include "4 failures" + _(run_result.stdout).must_include "4 controls have error" end end end diff --git a/test/functional/inspec_shell_test.rb b/test/functional/inspec_shell_test.rb index 1f021c26f..f0affdce7 100644 --- a/test/functional/inspec_shell_test.rb +++ b/test/functional/inspec_shell_test.rb @@ -224,7 +224,7 @@ describe "inspec shell tests" do it "runs controls having skipped tests with enhanced_outcomes option" do skip_windows! # Breakage confirmed out = assert_shell_c("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 101 , false, "", true) - _(out.stdout).must_include "Not Reviewed" + _(out.stdout).must_include "not reviewed" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "1 skipped" @@ -233,7 +233,7 @@ describe "inspec shell tests" do it "runs zero impact controls with enhanced_outcomes option" do skip_windows! # Breakage confirmed out = assert_shell_c("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, false, "", true) - _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "not applicable" _(out.stdout).must_include "1 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "0 skipped" @@ -242,11 +242,20 @@ describe "inspec shell tests" do it "runs zero impact controls with skipped test and enhanced_outcomes option" do skip_windows! # Breakage confirmed out = assert_shell_c("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 101, false, "", true) - _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "not applicable" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "1 skipped" end + + it "runs control with error and enhanced_outcomes option" do + skip_windows! # Breakage confirmed + out = assert_shell_c("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { must_bot exist } end end", 100, false, "", true) + _(out.stdout).must_include "has error" + _(out.stdout).must_include "0 successful" + _(out.stdout).must_include "1 failure" + _(out.stdout).must_include "0 skipped" + end end # Pry does not support STDIN from windows currently. Skipping these for now. @@ -381,7 +390,7 @@ describe "inspec shell tests" do it "runs controls having skipped tests with enhanced_outcomes option" do out = do_shell("control \"test\" do \n only_if { false }\n describe file(\"#{__FILE__}\") do it { should exist } end end", 0 , "", true) - _(out.stdout).must_include "Not Reviewed" + _(out.stdout).must_include "not reviewed" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "1 skipped" @@ -389,7 +398,7 @@ describe "inspec shell tests" do it "runs zero impact controls with enhanced_outcomes option" do out = do_shell("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, "", true) - _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "not applicable" _(out.stdout).must_include "1 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "0 skipped" @@ -397,7 +406,7 @@ describe "inspec shell tests" do it "runs zero impact controls with skipped test and enhanced_outcomes option" do out = do_shell("control \"test\" do \n impact 0.0 \n only_if { false } \n describe file(\"#{__FILE__}\") do it { should exist } end end", 0, "", true) - _(out.stdout).must_include "Not Applicable" + _(out.stdout).must_include "not applicable" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "0 failures" _(out.stdout).must_include "1 skipped" @@ -405,7 +414,7 @@ describe "inspec shell tests" do it "runs control with error and enhanced_outcomes option" do out = do_shell("control \"test\" do \n impact 0.0 \n describe file(\"#{__FILE__}\") do it { must_bot exist } end end", 0, "", true) - _(out.stdout).must_include "Error" + _(out.stdout).must_include "has error" _(out.stdout).must_include "0 successful" _(out.stdout).must_include "1 failure" _(out.stdout).must_include "0 skipped" From 1fc0076f1a2653bb5bf6b936e341dd0f16b2ea73 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 27 Jun 2022 16:56:18 +0530 Subject: [PATCH 68/93] Enhanced outcomes changes for streaming progress bar reporter Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 2 +- lib/inspec/reporters/cli.rb | 2 +- .../streaming_reporter.rb | 43 ++++++++---- .../controls/example.rb | 67 +++++++++++++++++++ .../enhanced-outcomes-test/inspec.yml | 10 +++ ...inspec_exec_streaming_progress_bar_test.rb | 16 +++++ test/functional/inspec_exec_test.rb | 30 +++++---- 7 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb create mode 100644 test/fixtures/profiles/enhanced-outcomes-test/inspec.yml diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index 44afe858a..00eff21ab 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -126,7 +126,7 @@ module Inspec::Formatters elsif control_has_all_tests_skipped(control) { name: "Not Reviewed", abbrev: "N/R" } elsif control[:results] && control[:results].any? { |r| r[:status] == "failed" } - { name: "Failed", abbrev: "fail"} + { name: "Failed", abbrev: "fail" } else { name: "Passed", abbrev: "pass" } end diff --git a/lib/inspec/reporters/cli.rb b/lib/inspec/reporters/cli.rb index b85fa2cc8..82b48ff31 100644 --- a/lib/inspec/reporters/cli.rb +++ b/lib/inspec/reporters/cli.rb @@ -366,7 +366,7 @@ module Inspec::Reporters format_with_color(failed_color, failed_str), format_with_color(not_rev_color, not_rev_str), format_with_color(not_app_color, not_app_str), - format_with_color(error_color, error_str), + format_with_color(error_color, error_str) ) output(s) if summary["total"] > 0 end diff --git a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb index 7eed0ac67..7cc137ac2 100644 --- a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +++ b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb @@ -20,6 +20,9 @@ module InspecPlugins::StreamingReporterProgressBar "passed" => "\033[0;1;32m", "skipped" => "\033[0;37m", "reset" => "\033[0m", + "error" => "\033[34m", + "not_applicable" => "\033[36m", + "not_reviewed" => "\033[33m", }.freeze # Most currently available Windows terminals have poor support @@ -28,6 +31,9 @@ module InspecPlugins::StreamingReporterProgressBar "failed" => "[FAIL]", "skipped" => "[SKIP]", "passed" => "[PASS]", + "error" => " [ERROR] ", + "not_applicable" => " [N/A] ", + "not_reviewed" => " [N/R] ", }.freeze else # Extended colors for everyone else @@ -36,6 +42,9 @@ module InspecPlugins::StreamingReporterProgressBar "passed" => "\033[38;5;41m", "skipped" => "\033[38;5;247m", "reset" => "\033[0m", + "error" => "\033[0;38;5;21m", + "not_applicable" => "\033[0;38;5;117m", + "not_reviewed" => "\033[0;38;5;214m", }.freeze # Groovy UTF-8 characters for everyone else... @@ -44,6 +53,9 @@ module InspecPlugins::StreamingReporterProgressBar "failed" => "× [FAILED] ", "skipped" => "↺ [SKIPPED]", "passed" => "✔ [PASSED] ", + "error" => "× [ERROR] ", + "not_applicable" => " [N/A] ", + "not_reviewed" => " [N/R] ", }.freeze end @@ -71,34 +83,37 @@ module InspecPlugins::StreamingReporterProgressBar control_id = notification.example.metadata[:id] title = notification.example.metadata[:title] full_description = notification.example.metadata[:full_description] - control_impact = notification.example.metadata[:impact] set_status_mapping(control_id, status) collect_notifications(notification, control_id, status) control_ended = control_ended?(control_id) if control_ended - add_enhanced_outcomes(control_id) if enhanced_outcomes - show_progress(control_id, title, full_description, control_impact) + control_outcome = add_enhanced_outcomes(control_id) if enhanced_outcomes + show_progress(control_id, title, full_description, control_outcome) end end - def show_progress(control_id, title, full_description, control_impact) + def show_progress(control_id, title, full_description, control_outcome) @bar ||= ProgressBar.new(controls_count, :bar, :counter, :percentage) sleep 0.1 @bar.increment! - @bar.puts format_it(control_id, title, full_description, control_impact) + @bar.puts format_it(control_id, title, full_description, control_outcome) rescue StandardError => e raise "Exception in Progress Bar streaming reporter: #{e}" end - def format_it(control_id, title, full_description, control_impact) - control_status = if @status_mapping[control_id].include? "failed" - "failed" - elsif @status_mapping[control_id].include? "passed" - "passed" - else - @status_mapping[control_id].include? "skipped" - "skipped" - end + def format_it(control_id, title, full_description, control_outcome) + if control_outcome + control_status = control_outcome[:name].downcase.gsub(" ", "_") + else + control_status = if @status_mapping[control_id].include? "failed" + "failed" + elsif @status_mapping[control_id].include? "passed" + "passed" + else + @status_mapping[control_id].include? "skipped" + "skipped" + end + end indicator = INDICATORS[control_status] message_to_format = "" message_to_format += "#{indicator} " diff --git a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb new file mode 100644 index 000000000..ae9d2951b --- /dev/null +++ b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb @@ -0,0 +1,67 @@ +# Error +control "tmp-1.0.1" do + impact 0.7 + describe file("/tmp") do + it { should_bot be_directory } + end +end + +control "tmp-1.0.2" do + impact 0.0 + describe file("/tmp") do + it { should_bot be_directory } + end +end + +# Not Applicable +control "tmp-2.0.1" do + impact 0.0 + describe file("/tmp") do + it { should be_directory } + end +end + +control "tmp-2.0.2" do + impact 0.0 + only_if { false } + describe file("/tmp") do + it { should be_directory } + end +end + +# Not Reviewed +control "tmp-3.0.1" do + only_if { false } + describe file("/tmp") do + it { should be_directory } + end +end + +control "tmp-3.0.2" do + only_if { false } + describe file("/tmp") do + it { should_bot be_directory } + end +end + +# Failed +control "tmp-4.0" do + impact 0.7 + title "Create /tmp directory" + desc "An optional description..." + describe file("/tmp") do + it { should_not be_directory } + it { should be_directory } + end +end + +# Passed +control "tmp-5.0" do + impact 0.7 + title "Create /tmp directory" + desc "An optional description..." + describe file("/tmp") do + it { should be_directory } + it { should exist } + end +end diff --git a/test/fixtures/profiles/enhanced-outcomes-test/inspec.yml b/test/fixtures/profiles/enhanced-outcomes-test/inspec.yml new file mode 100644 index 000000000..ae4d39f20 --- /dev/null +++ b/test/fixtures/profiles/enhanced-outcomes-test/inspec.yml @@ -0,0 +1,10 @@ +name: enhanced-outcomes-test +title: InSpec Profile +maintainer: The Authors +copyright: The Authors +copyright_email: you@example.com +license: Apache-2.0 +summary: An InSpec Compliance Profile +version: 0.1.0 +supports: + platform: os \ No newline at end of file diff --git a/test/functional/inspec_exec_streaming_progress_bar_test.rb b/test/functional/inspec_exec_streaming_progress_bar_test.rb index 9f2bdfa38..8f5a3f0b6 100644 --- a/test/functional/inspec_exec_streaming_progress_bar_test.rb +++ b/test/functional/inspec_exec_streaming_progress_bar_test.rb @@ -77,4 +77,20 @@ describe "inspec exec with streaming progress bar reporter" do assert_exit_code 101, out end + it "shows enhanced_outcomes with enhanced_outcomes flag" do + skip_windows! + + out = inspec("exec " + File.join(profile_path, "enhanced-outcomes-test") + " --no-create-lockfile --reporter progress-bar --enhanced-outcomes") + _(out.stderr).must_include "[ERROR] tmp-1.0.1" + _(out.stderr).must_include "[ERROR] tmp-1.0.2" + _(out.stderr).must_include "[N/A] tmp-2.0.1" + _(out.stderr).must_include "[N/A] tmp-2.0.2" + _(out.stderr).must_include "[N/R] tmp-3.0.1" + _(out.stderr).must_include "[N/R] tmp-3.0.2" + _(out.stderr).must_include "[N/R] tmp-3.0.2" + _(out.stderr).must_include "[FAILED] tmp-4.0" + _(out.stderr).must_include "[PASSED] tmp-5.0" + assert_exit_code 100, out + end + end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 1e0f4d212..3d07b3f4f 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1334,29 +1334,35 @@ EOT describe "when running profile with enhanced_outcomes option" do let(:run_result) { run_inspec_process("exec #{profile} --no-create-lockfile", enhanced_outcomes: true) } - let(:profile) { "#{profile_path}/only_if/skip-control" } + let(:profile) { "#{profile_path}/enhanced-outcomes-test" } it "should evaluate all test controls correctly" do _(run_result.stderr).must_be_empty end it "should show enhanced_outcomes for skipped tests in controls" do - _(run_result.stdout).must_include "6 skipped" - _(run_result.stdout).must_include "5 controls not reviewed" + _(run_result.stdout).must_include "3 skipped" + _(run_result.stdout).must_include "2 controls not reviewed" + _(run_result.stdout).must_include "N/R" end it "should show enhanced_outcomes for controls with impact 0" do - _(run_result.stdout).must_include "6 skipped" - _(run_result.stdout).must_include "1 control not applicable" + _(run_result.stdout).must_include "3 skipped" + _(run_result.stdout).must_include "2 controls not applicable" + _(run_result.stdout).must_include "N/A" end - end - - describe "when running profile with errors and enhanced_outcomes option" do - let(:run_result) { run_inspec_process("exec #{profile} --no-create-lockfile", enhanced_outcomes: true) } - let(:profile) { "#{profile_path}/exception-in-control" } it "should show enhanced_outcomes for controls with errors" do - _(run_result.stdout).must_include "4 failures" - _(run_result.stdout).must_include "4 controls have error" + _(run_result.stdout).must_include "3 failures" + _(run_result.stdout).must_include "2 controls have error" + _(run_result.stdout).must_include "ERR" + end + + it "should show enhanced_outcomes for controls with failures" do + _(run_result.stdout).must_include "1 control failure" + end + + it "should show enhanced_outcomes for passed controls" do + _(run_result.stdout).must_include "1 successful control" end end end From 82ab13dbe41f139e307803026f4191365540bc3d Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Tue, 28 Jun 2022 16:33:04 +0530 Subject: [PATCH 69/93] Html reporter changes for enhanced outcomes and refactoring changes Signed-off-by: Nikita Mathur --- lib/inspec/enhanced_outcomes.rb | 18 ++++ lib/inspec/formatters/base.rb | 83 ++++++++++++------- lib/inspec/plugin/v2/plugin_types/reporter.rb | 1 + .../v2/plugin_types/streaming_reporter.rb | 10 +-- lib/inspec/reporters/cli.rb | 10 +-- lib/inspec/run_data/control.rb | 6 ++ lib/inspec/run_data/statistics.rb | 10 ++- .../templates/body.html.erb | 8 +- .../templates/control.html.erb | 16 ++-- .../templates/default.css | 12 +++ .../templates/selector.html.erb | 8 +- .../streaming_reporter.rb | 2 +- 12 files changed, 132 insertions(+), 52 deletions(-) create mode 100644 lib/inspec/enhanced_outcomes.rb diff --git a/lib/inspec/enhanced_outcomes.rb b/lib/inspec/enhanced_outcomes.rb new file mode 100644 index 000000000..fd3e3a784 --- /dev/null +++ b/lib/inspec/enhanced_outcomes.rb @@ -0,0 +1,18 @@ +module Inspec + module EnhancedOutcomes + + def self.determine_status(results, impact) + if results.any? { |r| !r[:exception].nil? && !r[:backtrace].nil? } + "error" + elsif !impact.nil? && impact.to_f == 0.0 + "not_applicable" + elsif results.all? { |r| r[:status] == "skipped" } + "not_reviewed" + elsif results.any? { |r| r[:status] == "failed" } + "failed" + else + "passed" + end + end + end +end diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index 00eff21ab..82f1f737c 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -1,6 +1,7 @@ require "rspec/core" require "rspec/core/formatters/base_formatter" require "set" unless defined?(Set) +require "inspec/enhanced_outcomes" module Inspec::Formatters class Base < RSpec::Core::Formatters::BaseFormatter @@ -119,27 +120,13 @@ module Inspec::Formatters end def determine_control_enhanced_outcome(control) - if control_has_error(control) - { name: "Error", abbrev: "ERR" } - elsif control[:impact].to_f == 0.0 - { name: "Not Applicable", abbrev: "N/A" } - elsif control_has_all_tests_skipped(control) - { name: "Not Reviewed", abbrev: "N/R" } - elsif control[:results] && control[:results].any? { |r| r[:status] == "failed" } - { name: "Failed", abbrev: "fail" } + if control[:results] + Inspec::EnhancedOutcomes.determine_status(control[:results], control[:impact]) else - { name: "Passed", abbrev: "pass" } + "passed" end end - def control_has_all_tests_skipped(control) - control[:results] && control[:results].all? { |r| r[:status] == "skipped" } - end - - def control_has_error(control) - control[:results] && (control[:results].any? { |r| !r[:exception].nil? && !r[:backtrace].nil? }) - end - def all_unique_controls unique_controls = Set.new run_data[:profiles].each do |profile| @@ -150,25 +137,57 @@ module Inspec::Formatters end def statistics + error = 0 + not_applicable = 0 + not_reviewed = 0 failed = 0 - skipped = 0 passed = 0 + skipped = 0 + enhanced_outcomes_summary = {} + if enhanced_outcomes + all_unique_controls.each do |control| - all_unique_controls.each do |control| - next unless control[:results] - - if control[:results].any? { |r| r[:status] == "failed" } - failed += 1 - elsif control[:results].any? { |r| r[:status] == "skipped" } - skipped += 1 - else - passed += 1 + if control[:status] == "error" + error += 1 + elsif control[:status] == "not_applicable" + not_applicable += 1 + elsif control[:status] == "not_reviewed" + not_reviewed += 1 + elsif control[:results].any? { |r| r[:status] == "skipped" } + skipped += 1 + elsif control[:status] == "failed" + failed += 1 + elsif control[:status] == "passed" + passed += 1 + end end + total = error + not_applicable + not_reviewed + failed + passed + enhanced_outcomes_summary = { + not_applicable: { + total: not_applicable, + }, + not_reviewed: { + total: not_reviewed, + }, + error: { + total: error, + }, + } + else + all_unique_controls.each do |control| + next unless control[:results] + + if control[:results].any? { |r| r[:status] == "failed" } + failed += 1 + elsif control[:results].any? { |r| r[:status] == "skipped" } + skipped += 1 + else + passed += 1 + end + end + total = failed + passed + skipped end - - total = failed + passed + skipped - - { + final_summary = { total: total, passed: { total: passed, @@ -180,6 +199,8 @@ module Inspec::Formatters total: failed, }, } + + final_summary.merge!(enhanced_outcomes_summary) end def exception_message(exception) diff --git a/lib/inspec/plugin/v2/plugin_types/reporter.rb b/lib/inspec/plugin/v2/plugin_types/reporter.rb index 8dafa2cd2..d1782a0c3 100644 --- a/lib/inspec/plugin/v2/plugin_types/reporter.rb +++ b/lib/inspec/plugin/v2/plugin_types/reporter.rb @@ -7,6 +7,7 @@ module Inspec::Plugin::V2::PluginType include Inspec::Utils::RunDataFilters attr_reader :run_data + attr_accessor :enhanced_outcomes def initialize(config) @config = config diff --git a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb index 7de4b83bb..07eead8b1 100644 --- a/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +++ b/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb @@ -57,15 +57,15 @@ module Inspec::Plugin::V2::PluginType def add_enhanced_outcomes(control_id) if control_has_error(@notifications[control_id]) - { name: "Error", abbrev: "ERR" } + "error" elsif control_has_impact_zero(@notifications[control_id]) - { name: "Not Applicable", abbrev: "N/A" } + "not_applicable" elsif control_has_all_tests_skipped(@notifications[control_id]) - { name: "Not Reviewed", abbrev: "N/R" } + "not_reviewed" elsif control_has_any_tests_failed(@notifications[control_id]) - { name: "Failed", abbrev: "fail" } + "failed" else - { name: "Passed", abbrev: "pass" } + "passed" end end diff --git a/lib/inspec/reporters/cli.rb b/lib/inspec/reporters/cli.rb index 82b48ff31..a2c26c39b 100644 --- a/lib/inspec/reporters/cli.rb +++ b/lib/inspec/reporters/cli.rb @@ -319,13 +319,13 @@ module Inspec::Reporters all_unique_controls.each do |control| next if control[:status].empty? - if control[:status][:name] == "Failed" + if control[:status] == "failed" failed += 1 - elsif control[:status][:name] == "Error" + elsif control[:status] == "error" error += 1 - elsif control[:status][:name] == "Not Reviewed" + elsif control[:status] == "not_reviewed" not_reviewed += 1 - elsif control[:status][:name] == "Not Applicable" + elsif control[:status] == "not_applicable" not_applicable += 1 else passed += 1 @@ -483,7 +483,7 @@ module Inspec::Reporters if impact.nil? "unknown" else - status[:name].downcase.gsub(" ", "_") + status end end diff --git a/lib/inspec/run_data/control.rb b/lib/inspec/run_data/control.rb index 4bd42f482..3791300f0 100644 --- a/lib/inspec/run_data/control.rb +++ b/lib/inspec/run_data/control.rb @@ -1,3 +1,5 @@ +require "inspec/enhanced_outcomes" + module Inspec class RunData Control = Struct.new( @@ -31,6 +33,10 @@ module Inspec ].each do |field| self[field] = raw_ctl_data[field] end + + def status + Inspec::EnhancedOutcomes.determine_status(results, impact) + end end end diff --git a/lib/inspec/run_data/statistics.rb b/lib/inspec/run_data/statistics.rb index 8b4a9d2f9..104f28828 100644 --- a/lib/inspec/run_data/statistics.rb +++ b/lib/inspec/run_data/statistics.rb @@ -16,14 +16,20 @@ module Inspec :total, :passed, :skipped, - :failed + :failed, + :not_reviewed, + :not_applicable, + :error ) do include HashLikeStruct def initialize(raw_stat_ctl_data) self.total = raw_stat_ctl_data[:total] self.passed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:passed][:total]) - self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total]) self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total]) + self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total]) if raw_stat_ctl_data[:skipped] + self.not_reviewed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_reviewed][:total]) if raw_stat_ctl_data[:not_reviewed] + self.not_applicable = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_applicable][:total]) if raw_stat_ctl_data[:not_applicable] + self.error = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:error][:total]) if raw_stat_ctl_data[:error] end end class Controls diff --git a/lib/plugins/inspec-reporter-html2/templates/body.html.erb b/lib/plugins/inspec-reporter-html2/templates/body.html.erb index daa827abf..206fe6948 100644 --- a/lib/plugins/inspec-reporter-html2/templates/body.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/body.html.erb @@ -36,8 +36,14 @@ Control Statistics

Control Statistics

Passed:<%= run_data.statistics.controls.passed.total %> - Skipped:<%= run_data.statistics.controls.skipped.total %> Failed:<%= run_data.statistics.controls.failed.total %> + <% if enhanced_outcomes %> + Not Reviewed:<%= run_data.statistics.controls.not_reviewed.total %> + Not Applicable:<%= run_data.statistics.controls.not_applicable.total %> + Error:<%= run_data.statistics.controls.error.total %> + <% else %> + Skipped:<%= run_data.statistics.controls.skipped.total %> + <% end %> Duration:<%= run_data.statistics.duration %> seconds Time Finished:<%= Time.now %> diff --git a/lib/plugins/inspec-reporter-html2/templates/control.html.erb b/lib/plugins/inspec-reporter-html2/templates/control.html.erb index b011649be..6e9183a4e 100644 --- a/lib/plugins/inspec-reporter-html2/templates/control.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/control.html.erb @@ -1,11 +1,15 @@ <% slugged_id = control.id.tr(" ", "_") %> <% - # Determine status of control - status = "passed" - if control.results.any? { |r| r.status == "failed" } - status = "failed" - elsif control.results.any? { |r| r.status == "skipped" } - status = "skipped" + if enhanced_outcomes + status = control.status + else + # Determine status of control + status = "passed" + if control.results.any? { |r| r.status == "failed" } + status = "failed" + elsif control.results.any? { |r| r.status == "skipped" } + status = "skipped" + end end %> diff --git a/lib/plugins/inspec-reporter-html2/templates/default.css b/lib/plugins/inspec-reporter-html2/templates/default.css index 7e12ac01e..0e161cb92 100644 --- a/lib/plugins/inspec-reporter-html2/templates/default.css +++ b/lib/plugins/inspec-reporter-html2/templates/default.css @@ -60,6 +60,18 @@ pre code { .result-metadata .status-skipped div { background-color: grey; } +.control-metadata .status-error div, +.result-metadata .status-error div { + background-color: rgb(63, 15, 183); +} +.control-metadata .status-not_applicable div, +.result-metadata .status-not_applicable div { + background-color: rgb(135, 206, 250); +} +.control-metadata .status-not_reviewed div, +.result-metadata .status-not_reviewed div { + background-color: rgb(255, 194, 0); +} .result-metadata, .control-metadata { margin: 0 0 0 5%; diff --git a/lib/plugins/inspec-reporter-html2/templates/selector.html.erb b/lib/plugins/inspec-reporter-html2/templates/selector.html.erb index b340403dd..062e55166 100644 --- a/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +++ b/lib/plugins/inspec-reporter-html2/templates/selector.html.erb @@ -1,8 +1,14 @@

Display controls that are:

- + <% if enhanced_outcomes %> + + + + <% else %> + + <% end %>

Display profiles that are:

diff --git a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb index 7cc137ac2..18b4eae3c 100644 --- a/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +++ b/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb @@ -103,7 +103,7 @@ module InspecPlugins::StreamingReporterProgressBar def format_it(control_id, title, full_description, control_outcome) if control_outcome - control_status = control_outcome[:name].downcase.gsub(" ", "_") + control_status = control_outcome else control_status = if @status_mapping[control_id].include? "failed" "failed" From a96e226bef463aa83a045bf943463fa2ccbb0240 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Tue, 28 Jun 2022 16:58:25 +0530 Subject: [PATCH 70/93] Exit code logic to consider error count and build fixes Signed-off-by: Nikita Mathur --- lib/inspec/formatters/base.rb | 6 ++++-- lib/inspec/runner_rspec.rb | 4 ++-- .../functional/inspec_exec_streaming_progress_bar_test.rb | 8 +++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/inspec/formatters/base.rb b/lib/inspec/formatters/base.rb index 82f1f737c..bc397722f 100644 --- a/lib/inspec/formatters/base.rb +++ b/lib/inspec/formatters/base.rb @@ -153,13 +153,15 @@ module Inspec::Formatters not_applicable += 1 elsif control[:status] == "not_reviewed" not_reviewed += 1 - elsif control[:results].any? { |r| r[:status] == "skipped" } - skipped += 1 elsif control[:status] == "failed" failed += 1 elsif control[:status] == "passed" passed += 1 end + + # added this additionally because stats summary is also used for determining exit code in runner rspec + skipped += 1 if control[:results].any? { |r| r[:status] == "skipped" } + end total = error + not_applicable + not_reviewed + failed + passed enhanced_outcomes_summary = { diff --git a/lib/inspec/runner_rspec.rb b/lib/inspec/runner_rspec.rb index fde0c975b..3d34d6731 100644 --- a/lib/inspec/runner_rspec.rb +++ b/lib/inspec/runner_rspec.rb @@ -107,11 +107,11 @@ module Inspec stats = @formatter.results[:statistics][:controls] load_failures = @formatter.results[:profiles]&.select { |p| p[:status] == "failed" }&.any? skipped = @formatter.results.dig(:profiles, 0, :status) == "skipped" - if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures + if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures && (stats[:error] && stats[:error][:total] == 0) # placed error count condition because of enhanced outcomes 0 elsif load_failures @conf["distinct_exit"] ? 102 : 1 - elsif stats[:failed][:total] > 0 + elsif stats[:failed][:total] > 0 || (stats[:error] && stats[:error][:total] > 0) @conf["distinct_exit"] ? 100 : 1 elsif stats[:skipped][:total] > 0 || skipped @conf["distinct_exit"] ? 101 : 0 diff --git a/test/functional/inspec_exec_streaming_progress_bar_test.rb b/test/functional/inspec_exec_streaming_progress_bar_test.rb index 8f5a3f0b6..ab784add2 100644 --- a/test/functional/inspec_exec_streaming_progress_bar_test.rb +++ b/test/functional/inspec_exec_streaming_progress_bar_test.rb @@ -28,8 +28,6 @@ describe "inspec exec with streaming progress bar reporter" do it "can execute a profile with dependent profiles" do profile = File.join(profile_path, "dependencies", "inheritance") out = inspec("exec " + profile + " --reporter progress-bar --no-create-lockfile") - _(out.stderr).must_include "[100.00%]" - _(out.stderr).must_include "[6/6]" assert_exit_code 0, out end @@ -49,10 +47,10 @@ describe "inspec exec with streaming progress bar reporter" do end it "can execute multiple profiles" do - out = inspec("exec " + File.join(profile_path, "dependencies", "inheritance") + " " + File.join(profile_path, "controls-option-test") + " --no-create-lockfile --reporter progress-bar") + out = inspec("exec " + File.join(profile_path, "control-tags") + " " + File.join(profile_path, "controls-option-test") + " --no-create-lockfile --reporter progress-bar") _(out.stderr).must_include "[100.00%]" - _(out.stderr).must_include "[11/11]" - assert_exit_code 0, out + _(out.stderr).must_include "[10/10]" + assert_exit_code 100, out end it "can execute and print proper output when tests are failed" do From dfd66012b455195ec9e30a5e0b84351d0b187cac Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 30 Jun 2022 14:09:59 +0530 Subject: [PATCH 71/93] Enhanced outcomes test profiles made consistent for testing across diff platforms Signed-off-by: Nikita Mathur --- .../controls/example.rb | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb index ae9d2951b..79e7bf035 100644 --- a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb +++ b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb @@ -1,67 +1,62 @@ # Error control "tmp-1.0.1" do impact 0.7 - describe file("/tmp") do - it { should_bot be_directory } + describe "a.1" do + it { should_bot "a.1" } end end control "tmp-1.0.2" do impact 0.0 - describe file("/tmp") do - it { should_bot be_directory } + describe "a.2" do + it { should_bot "a.2" } end end # Not Applicable control "tmp-2.0.1" do impact 0.0 - describe file("/tmp") do - it { should be_directory } + describe "b.1" do + it { should cmp "b.1" } end end control "tmp-2.0.2" do impact 0.0 only_if { false } - describe file("/tmp") do - it { should be_directory } + describe "b.2" do + it { should cmp "b.2" } end end # Not Reviewed control "tmp-3.0.1" do only_if { false } - describe file("/tmp") do - it { should be_directory } + describe "c.1" do + it { should cmp "c.1" } end end control "tmp-3.0.2" do only_if { false } - describe file("/tmp") do - it { should_bot be_directory } + describe "c.2" do + it { should_bot "c.2" } end end # Failed control "tmp-4.0" do impact 0.7 - title "Create /tmp directory" - desc "An optional description..." - describe file("/tmp") do - it { should_not be_directory } - it { should be_directory } + describe "d.1" do + it { should_not cmp "d.1" } + it { should cmp "d.1" } end end # Passed control "tmp-5.0" do impact 0.7 - title "Create /tmp directory" - desc "An optional description..." - describe file("/tmp") do - it { should be_directory } - it { should exist } + describe "e.1" do + it { should cmp "e.1" } end end From a9fae7cfe2abc126a32c32b64ca64143471d8b30 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 29 Jun 2022 15:24:27 +0530 Subject: [PATCH 72/93] Added enhanced outcomes option to schema subcommand and updated schema Signed-off-by: Nikita Mathur --- lib/inspec/cli.rb | 6 +- lib/inspec/schema.rb | 91 +++++++++++++++++++++++++++ lib/inspec/schema/exec_json.rb | 80 ++++++++++++++++++++++- lib/inspec/schema/output_schema.rb | 5 +- lib/inspec/schema/profile_json.rb | 46 ++++++++++++++ test/functional/inspec_schema_test.rb | 8 +++ 6 files changed, 231 insertions(+), 5 deletions(-) diff --git a/lib/inspec/cli.rb b/lib/inspec/cli.rb index 47f91ec78..894dd46bc 100644 --- a/lib/inspec/cli.rb +++ b/lib/inspec/cli.rb @@ -463,11 +463,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI pretty_handle_exception(e) end + option :enhanced_outcomes, type: :boolean, + desc: "Show enhanced outcomes output" desc "schema NAME", "print the JSON schema", hide: true def schema(name) require "inspec/schema/output_schema" - - puts Inspec::Schema::OutputSchema.json(name) + o = config + puts Inspec::Schema::OutputSchema.json(name, o) rescue StandardError => e puts e puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}" diff --git a/lib/inspec/schema.rb b/lib/inspec/schema.rb index c1d405d54..727e2f0cb 100644 --- a/lib/inspec/schema.rb +++ b/lib/inspec/schema.rb @@ -111,6 +111,43 @@ module Inspec }, }.freeze + CONTROL_ENHANCED_OUTCOME = { + "type" => "object", + "additionalProperties" => false, + "properties" => { + "id" => { "type" => "string" }, + "title" => { "type" => %w{string null} }, + "desc" => { "type" => %w{string null} }, + "descriptions" => { "type" => %w{array} }, + "impact" => { "type" => "number" }, + "status" => { + "enum" => %w{passed failed not_applicable not_reviewed error}, + "description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"), + }, + "refs" => REFS, + "tags" => TAGS, + "code" => { "type" => "string" }, + "source_location" => { + "type" => "object", + "properties" => { + "ref" => { "type" => "string" }, + "line" => { "type" => "number" }, + }, + }, + "results" => { "type" => "array", "items" => RESULT }, + "waiver_data" => { + "type" => "object", + "properties" => { + "skipped_due_to_waiver" => { "type" => "string" }, + "run" => { "type" => "boolean" }, + "message" => { "type" => "string" }, + "expiration_date" => { "type" => "string" }, + "justification" => { "type" => "string" }, + }, + }, + }, + }.freeze + SUPPORTS = { "type" => "object", "additionalProperties" => false, @@ -173,6 +210,45 @@ module Inspec }, }.freeze + PROFILE_ENHANCED_OUTCOME = { + "type" => "object", + "additionalProperties" => false, + "properties" => { + "name" => { "type" => "string" }, + "version" => { "type" => "string", "optional" => true }, + "sha256" => { "type" => "string", "optional" => false }, + + "title" => { "type" => "string", "optional" => true }, + "maintainer" => { "type" => "string", "optional" => true }, + "copyright" => { "type" => "string", "optional" => true }, + "copyright_email" => { "type" => "string", "optional" => true }, + "license" => { "type" => "string", "optional" => true }, + "summary" => { "type" => "string", "optional" => true }, + "status" => { "type" => "string", "optional" => false }, + "status_message" => { "type" => "string", "optional" => true }, + # skip_message is deprecated, status_message should be used to store the reason for skipping + "skip_message" => { "type" => "string", "optional" => true }, + + "supports" => { + "type" => "array", + "items" => SUPPORTS, + "optional" => true, + }, + "controls" => { + "type" => "array", + "items" => CONTROL_ENHANCED_OUTCOME, + }, + "groups" => { + "type" => "array", + "items" => CONTROL_GROUP, + }, + "attributes" => { # TODO: rename to inputs, refs #3802 + "type" => "array", + # TODO: more detailed specification needed + }, + }, + }.freeze + EXEC_JSON = { "type" => "object", "additionalProperties" => false, @@ -187,6 +263,20 @@ module Inspec }, }.freeze + EXEC_JSON_ENHANCED_OUTCOME = { + "type" => "object", + "additionalProperties" => false, + "properties" => { + "platform" => PLATFORM, + "profiles" => { + "type" => "array", + "items" => PROFILE_ENHANCED_OUTCOME, + }, + "statistics" => STATISTICS, + "version" => { "type" => "string" }, + }, + }.freeze + MIN_CONTROL = { "type" => "object", "additionalProperties" => false, @@ -228,6 +318,7 @@ module Inspec LIST = { "exec-json" => EXEC_JSON, "exec-jsonmin" => EXEC_JSONMIN, + "exec-json-enhanced-outcome" => EXEC_JSON_ENHANCED_OUTCOME, "platforms" => PLATFORMS, }.freeze diff --git a/lib/inspec/schema/exec_json.rb b/lib/inspec/schema/exec_json.rb index 3f104c1c2..c591cb81c 100644 --- a/lib/inspec/schema/exec_json.rb +++ b/lib/inspec/schema/exec_json.rb @@ -19,8 +19,8 @@ module Inspec # Lists the potential values for a control result CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", { "type" => "string", - "enum" => %w{passed failed skipped error}, - }, [], "The status of a control. Should be one of 'passed', 'failed', 'skipped', or 'error'.") + "enum" => %w{passed failed skipped}, + }, [], "The status of a control. Should be one of 'passed', 'failed', or 'skipped'.") # Represents the statistics/result of a control"s execution CONTROL_RESULT = Primitives::SchemaType.new("Control Result", { @@ -75,6 +75,36 @@ module Inspec }, }, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.") + # Represents a control produced with enhanced outcomes option + ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Exec JSON Control", { + "type" => "object", + "additionalProperties" => true, + "required" => %w{id title desc impact refs tags code source_location results}, + "properties" => { + "id" => Primitives.desc(Primitives::STRING, "The id."), + "title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), # Nullable string + "desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."), + "descriptions" => Primitives.desc(Primitives.array(CONTROL_DESCRIPTION.ref), "A set of additional descriptions. Example: the 'fix' text."), + "impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."), + "status" => { + "enum" => %w{passed failed not_applicable not_reviewed error}, + "description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"), + }, + "refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."), + "tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."), + "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."), + "source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."), + "results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q( + The set of all tests within the control and their results and findings. Example: + For Chef Inspec, if in the control's code we had the following: + describe sshd_config do + its('Port') { should cmp 22 } + end + The findings from this block would be appended to the results, as well as those of any other blocks within the control. + )), + }, + }, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.") + # Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019 # However, concessions were made to the reality of current reporters, specifically # with how description is omitted and version/inspec_version aren't as advertised online @@ -112,6 +142,40 @@ module Inspec }, }, [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.") + ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Exec JSON Profile", { + "type" => "object", + "additionalProperties" => true, + "required" => %w{name sha256 supports attributes groups controls}, + # Name is mandatory in inspec.yml. + # supports, controls, groups, and attributes are always present, even if empty + # sha256, status, status_message + "properties" => { + # These are provided in inspec.yml + "name" => Primitives.desc(Primitives::STRING, "The name - must be unique."), + "title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."), + "maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."), + "copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."), + "copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contact information of the copyright holder(s)."), + "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."), + "parent_profile" => Primitives.desc(Primitives::STRING, "The name of the parent profile if the profile is a dependency of another."), + "license" => Primitives.desc(Primitives::STRING, "The copyright license. Example: the full text or the name, such as 'Apache License, Version 2.0'."), + "summary" => Primitives.desc(Primitives::STRING, "The summary. Example: the Security Technical Implementation Guide (STIG) header."), + "version" => Primitives.desc(Primitives::STRING, "The version of the profile."), + "supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."), + "description" => Primitives.desc(Primitives::STRING, "The description - should be more detailed than the summary."), + "inspec_version" => Primitives.desc(Primitives::STRING, "The version of Inspec."), + + # These are generated at runtime, and all except status_message and skip_message are guaranteed + "sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."), + "status" => Primitives.desc(Primitives::STRING, "The status. Example: loaded."), # enum? loaded, failed, skipped + "status_message" => Primitives.desc(Primitives::STRING, "The reason for the status. Example: why it was skipped or failed to load."), + "skip_message" => Primitives.desc(Primitives::STRING, "The reason for skipping if it was skipped."), # Deprecated field - status_message should be used instead. + "controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings."), + "groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."), + "attributes" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used in the run."), + }, + }, [ENHANCED_OUTCOME_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.") + # Result of exec json. Top level value # TODO: Include the format of top level controls. This was omitted for lack of sufficient examples OUTPUT = Primitives::SchemaType.new("Exec JSON Output", { @@ -125,6 +189,18 @@ module Inspec "version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."), }, }, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.") + + ENHANCED_OUTCOME_OUTPUT = Primitives::SchemaType.new("Exec JSON Output", { + "type" => "object", + "additionalProperties" => true, + "required" => %w{platform profiles statistics version}, + "properties" => { + "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."), + "profiles" => Primitives.desc(Primitives.array(PROFILE.ref), "Information on the run(s) from the tool that generated the findings. Example: the findings."), + "statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."), + "version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."), + }, + }, [Primitives::PLATFORM, ENHANCED_OUTCOME_PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.") end end end diff --git a/lib/inspec/schema/output_schema.rb b/lib/inspec/schema/output_schema.rb index ad713a725..3d48f6566 100644 --- a/lib/inspec/schema/output_schema.rb +++ b/lib/inspec/schema/output_schema.rb @@ -30,6 +30,8 @@ module Inspec "profile-json" => OutputSchema.finalize(Schema::ProfileJson::PROFILE), "exec-json" => OutputSchema.finalize(Schema::ExecJson::OUTPUT), "exec-jsonmin" => OutputSchema.finalize(Schema::ExecJsonMin::OUTPUT), + "profile-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ProfileJson::ENHANCED_OUTCOME_PROFILE), + "exec-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ExecJson::ENHANCED_OUTCOME_OUTPUT), "platforms" => PLATFORMS, }.freeze @@ -37,7 +39,8 @@ module Inspec LIST.keys end - def self.json(name) + def self.json(name, opts) + name += "-enhanced-outcomes" if opts["enhanced_outcomes"] if !LIST.key?(name) raise("Cannot find schema #{name.inspect}.") elsif LIST[name].is_a?(Proc) diff --git a/lib/inspec/schema/profile_json.rb b/lib/inspec/schema/profile_json.rb index 5806a464e..d112bfbee 100644 --- a/lib/inspec/schema/profile_json.rb +++ b/lib/inspec/schema/profile_json.rb @@ -31,6 +31,28 @@ module Inspec }, }, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.") + # Represents a control with enhanced outcomes status information + ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Profile JSON Control", { + "type" => "object", + "additionalProperties" => true, + "required" => %w{id title desc impact tags code}, + "properties" => { + "id" => Primitives.desc(Primitives::STRING, "The id."), + "title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), + "desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."), + "descriptions" => Primitives.desc(CONTROL_DESCRIPTIONS.ref, "A set of additional descriptions. Example: the 'fix' text."), + "impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."), + "status" => { + "enum" => %w{passed failed not_applicable not_reviewed error}, + "description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"), + }, + "refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."), + "tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."), + "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."), + "source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."), + }, + }, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.") + # A profile that has not been run. PROFILE = Primitives::SchemaType.new("Profile JSON Profile", { "type" => "object", @@ -55,6 +77,30 @@ module Inspec "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."), # Can have depends, but NOT a parentprofile }, }, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.") + + ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Profile JSON Profile", { + "type" => "object", + "additionalProperties" => true, # Anything in the yaml will be put in here. LTTODO: Make this stricter! + "required" => %w{name supports controls groups sha256}, + "properties" => { + "name" => Primitives.desc(Primitives::STRING, "The name - must be unique."), + "supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."), + "controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls - contains no findings as the assessment has not yet occurred."), + "groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."), + "inputs" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used to be in the run."), + "sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."), + "status" => Primitives.desc(Primitives::STRING, "The status. Example: skipped."), + "generator" => Primitives.desc(Primitives::GENERATOR.ref, "The tool that generated this file. Example: Chef Inspec."), + "version" => Primitives.desc(Primitives::STRING, "The version of the profile."), + + # Other properties possible in inspec docs, but that aren"t guaranteed + "title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."), + "maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."), + "copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."), + "copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contract information of the copyright holder(s)."), + "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."), # Can have depends, but NOT a parentprofile + }, + }, [Primitives::SUPPORT, ENHANCED_OUTCOME_CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.") end end end diff --git a/test/functional/inspec_schema_test.rb b/test/functional/inspec_schema_test.rb index 12e0ee3d2..5766bdf89 100644 --- a/test/functional/inspec_schema_test.rb +++ b/test/functional/inspec_schema_test.rb @@ -27,4 +27,12 @@ describe "inspec schema" do _(json_output["definitions"]["Control_Result"]["properties"]["resource_id"]).wont_be_nil end end + + describe "validate schema of exec-json with enhanced_outcomes option" do + it "contains resource_id key" do + out = inspec("schema exec-json --enhanced-outcomes") + json_output = JSON.parse(out.stdout) + _(json_output["definitions"]["Exec_JSON_Control"]["properties"]["status"]).wont_be_nil + end + end end From cfbddb82a5d5eb325831890d162c1d7640770469 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 1 Jul 2022 14:17:35 +0530 Subject: [PATCH 73/93] Negative test added to check no status flag exist when enhanced outcomes flag is not used Signed-off-by: Nikita Mathur --- test/functional/inspec_schema_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/inspec_schema_test.rb b/test/functional/inspec_schema_test.rb index 5766bdf89..75b549bb9 100644 --- a/test/functional/inspec_schema_test.rb +++ b/test/functional/inspec_schema_test.rb @@ -25,6 +25,8 @@ describe "inspec schema" do out = inspec("schema exec-json") json_output = JSON.parse(out.stdout) _(json_output["definitions"]["Control_Result"]["properties"]["resource_id"]).wont_be_nil + # status value to be nil when not using enhanced outcomes flag + _(json_output["definitions"]["Exec_JSON_Control"]["properties"]["status"]).must_equal nil end end From b26506b741c20c739786850fa7f76b4347dee642 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Thu, 30 Jun 2022 18:51:48 +0530 Subject: [PATCH 74/93] Enhanced outcomes changes in json based and yaml reporter Signed-off-by: Nikita Mathur --- lib/inspec/reporters/json.rb | 4 +++- lib/inspec/reporters/yaml.rb | 4 +++- test/functional/helper.rb | 1 + test/functional/inspec_exec_json_test.rb | 17 ++++++++++++++ test/functional/inspec_exec_test.rb | 28 ++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/inspec/reporters/json.rb b/lib/inspec/reporters/json.rb index dbe584754..85b2f2917 100644 --- a/lib/inspec/reporters/json.rb +++ b/lib/inspec/reporters/json.rb @@ -114,7 +114,7 @@ module Inspec::Reporters def profile_controls(profile) (profile[:controls] || []).map { |c| - { + control_hash = { id: c[:id], title: c[:title], desc: c.dig(:descriptions, :default), @@ -130,6 +130,8 @@ module Inspec::Reporters waiver_data: c[:waiver_data] || {}, results: profile_results(c), } + control_hash.merge!({ status: c[:status] }) if enhanced_outcomes + control_hash } end diff --git a/lib/inspec/reporters/yaml.rb b/lib/inspec/reporters/yaml.rb index f327b5c4b..4cb44779a 100644 --- a/lib/inspec/reporters/yaml.rb +++ b/lib/inspec/reporters/yaml.rb @@ -3,7 +3,9 @@ require "yaml" module Inspec::Reporters class Yaml < Base def render - output(Inspec::Reporters::Json.new({ run_data: run_data }).report.to_yaml, false) + json_reporter_obj = Inspec::Reporters::Json.new({ run_data: run_data }) + json_reporter_obj.enhanced_outcomes = enhanced_outcomes + output(json_reporter_obj.report.to_yaml, false) end def report diff --git a/test/functional/helper.rb b/test/functional/helper.rb index 213de3cd3..87591611e 100644 --- a/test/functional/helper.rb +++ b/test/functional/helper.rb @@ -39,6 +39,7 @@ module FunctionalHelper let(:simple_inheritance) { File.join(profile_path, "simple-inheritance") } let(:sensitive_profile) { File.join(examples_path, "profile-sensitive") } let(:config_dir_path) { File.join(mock_path, "config_dirs") } + let(:enhanced_outcome_profile) { "#{profile_path}/enhanced-outcomes-test" } let(:dst) do # create a temporary path, but we only want an auto-clean helper diff --git a/test/functional/inspec_exec_json_test.rb b/test/functional/inspec_exec_json_test.rb index bf4d5271b..0b14e6dfb 100644 --- a/test/functional/inspec_exec_json_test.rb +++ b/test/functional/inspec_exec_json_test.rb @@ -547,4 +547,21 @@ describe "inspec exec with json formatter" do end end + + describe "when running a profile with enhanced_outcomes option" do + it "can execute a profile and validate the json schema" do + out = inspec("exec " + enhanced_outcome_profile + " --reporter json --no-create-lockfile --enhanced-outcomes") + data = JSON.parse(out.stdout) + sout = inspec("schema exec-json") + schema = JSONSchemer.schema(sout.stdout) + _(schema.validate(data).to_a).must_equal [] + _(out.stderr).must_equal "" + _(data["profiles"].first["controls"][0]["status"]).must_equal "error" + _(data["profiles"].first["controls"][2]["status"]).must_equal "not_applicable" + _(data["profiles"].first["controls"][4]["status"]).must_equal "not_reviewed" + _(data["profiles"].first["controls"][6]["status"]).must_equal "failed" + _(data["profiles"].first["controls"][7]["status"]).must_equal "passed" + assert_exit_code 100, out + end + end end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 3d07b3f4f..14860af2d 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1365,4 +1365,32 @@ EOT _(run_result.stdout).must_include "1 successful control" end end + + describe "when running profile with enhanced_outcomes option and yaml reporter" do + let(:run_result) { run_inspec_process("exec #{profile} --no-create-lockfile --reporter yaml", enhanced_outcomes: true) } + let(:profile) { "#{profile_path}/enhanced-outcomes-test" } + it "should evaluate all test controls correctly" do + _(run_result.stderr).must_be_empty + end + + it "should show enhanced_outcomes for skipped tests in controls" do + _(run_result.stdout).must_include ":status: not_reviewed" + end + + it "should show enhanced_outcomes for controls with impact 0" do + _(run_result.stdout).must_include ":status: not_applicable" + end + + it "should show enhanced_outcomes for controls with errors" do + _(run_result.stdout).must_include ":status: error" + end + + it "should show enhanced_outcomes for controls with failures" do + _(run_result.stdout).must_include ":status: failed" + end + + it "should show enhanced_outcomes for passed controls" do + _(run_result.stdout).must_include ":status: passed" + end + end end From f59fd6adfb86666cd56dd64fe5c809a1b8d4ac2e Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 1 Jul 2022 16:49:11 +0530 Subject: [PATCH 75/93] Doc changes for enhanced outcomes Signed-off-by: Nikita Mathur --- docs-chef-io/content/inspec/cli.md | 11 +++++++++++ docs-chef-io/content/inspec/reporters.md | 16 ++++++++++++++++ .../reporter_outcome_progress_bar-min.png | Bin 0 -> 49753 bytes ...tcome_progress_bar_enhanced_outcomes-min.png | Bin 0 -> 44806 bytes 4 files changed, 27 insertions(+) create mode 100644 docs-chef-io/static/images/inspec/reporter_outcome_progress_bar-min.png create mode 100644 docs-chef-io/static/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes-min.png diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index f0ad9eac8..dc46f5faf 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -403,6 +403,8 @@ This subcommand has the following additional options: Whether to use disable sspi authentication, defaults to false (WinRM). * `--winrm-transport=WINRM_TRANSPORT` Specify which transport to use, defaults to negotiate (WinRM). +* `--enhanced-outcomes` + Includes enhanced outcome of controls in report data. ## habitat @@ -503,6 +505,13 @@ This subcommand has the following syntax: inspec schema NAME ``` +### Options + +This subcommand has the following additional options: + +* `--enhanced-outcomes` + Includes enhanced outcome of controls in report data. + ## shell Open an interactive debugging shell. @@ -603,6 +612,8 @@ This subcommand has the following additional options: Whether to use disable sspi authentication, defaults to false (WinRM). * `--winrm-transport=WINRM_TRANSPORT` Specify which transport to use, defaults to negotiate (WinRM). +* `--enhanced-outcomes` + Includes enhanced outcome of controls in report data. ## supermarket diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index 0d025a98d..ba63ee51c 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -126,6 +126,14 @@ The following are CLI options that may be used to modify reporter behavior. Many : This may be used to limit the size of reports when failure messages are exceptionally large. +`--enhanced-outcomes` + +: Includes enhanced outcome of controls in report data. + +: The control level status outcomes are Passed, Failed, Not Applicable (N/A), Not Reviewed (N/R) or Error (ERR). + +: Only supported for CLI, progress-bar, html2, json, json-automate, automate, and yaml reporters. + ## Supported Reporters The following are the current supported reporters: @@ -182,6 +190,14 @@ This reporter is very condensed and gives you a `.`(pass), `f`(fail), or `*`(ski This reporter outputs real-time progress of a running InSpec profile using a progress bar and prints running control's ID with an indicator of control's status (Passed, failed or skipped). +For example: + +![Progress Bar Reporter Outcome](/images/inspec/reporter_outcome_progress_bar.png) + +And reporter outcome with `--enhanced-outcomes` option : + +![Progress Bar Reporter Outcome with enhanced outcomes](/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes.png) + ### json-rspec This reporter includes all information from the rspec runner. Unlike the json reporter this includes rspec specific details. diff --git a/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar-min.png b/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar-min.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a2d1b86771df9713e56580c34dfa500ee3cb2a GIT binary patch literal 49753 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

)V;S)fTJ}k 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

MJ=xbHXu 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 Date: Fri, 1 Jul 2022 16:50:55 +0530 Subject: [PATCH 76/93] Grammar check for schema options section in cli doc Signed-off-by: Nikita Mathur --- docs-chef-io/content/inspec/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index dc46f5faf..7e56e2a12 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -507,7 +507,7 @@ inspec schema NAME ### Options -This subcommand has the following additional options: +This subcommand has the following additional option: * `--enhanced-outcomes` Includes enhanced outcome of controls in report data. From 7272d9e4df1ce757a91f9ba10e8db98daad3f488 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 1 Jul 2022 16:54:15 +0530 Subject: [PATCH 77/93] Enhanced outcome example images renamed Signed-off-by: Nikita Mathur --- ...ar-min.png => reporter_outcome_progress_bar.png} | Bin ...rter_outcome_progress_bar_enhanced_outcomes.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename docs-chef-io/static/images/inspec/{reporter_outcome_progress_bar-min.png => reporter_outcome_progress_bar.png} (100%) rename docs-chef-io/static/images/inspec/{reporter_outcome_progress_bar_enhanced_outcomes-min.png => reporter_outcome_progress_bar_enhanced_outcomes.png} (100%) diff --git a/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar-min.png b/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar.png similarity index 100% rename from docs-chef-io/static/images/inspec/reporter_outcome_progress_bar-min.png rename to docs-chef-io/static/images/inspec/reporter_outcome_progress_bar.png diff --git a/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes-min.png b/docs-chef-io/static/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes.png similarity index 100% rename from docs-chef-io/static/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes-min.png rename to docs-chef-io/static/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes.png From 974045d5d7003c3e985a62005c8e623bfb452d16 Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Fri, 1 Jul 2022 19:02:14 +0530 Subject: [PATCH 78/93] Content Review Signed-off-by: Deepa Kumaraswamy --- docs-chef-io/content/inspec/cli.md | 52 +++++----- docs-chef-io/content/inspec/reporters.md | 121 +++++++++++------------ 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 7e56e2a12..149a72f3e 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -38,7 +38,7 @@ This subcommand has the following additional options: * `-o`, `--output=OUTPUT` Save the archive to a path. * `--overwrite`, `--no-overwrite` - Overwrite existing archive. + Overwrite existing archives. * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--tar`, `--no-tar` @@ -62,7 +62,7 @@ inspec automate SUBCOMMAND ## check -Verify the metadata in the `inspec.yml` file, verify that control blocks have the correct fields (title, description, impact) defined that all controls have visible tests, and the controls are not using deprecated InSpec DSL code. +Verify the metadata in the `inspec.yml` file, verify that control blocks have the correct fields (title, description, impact), and define that all controls have visible tests and the controls are not using deprecated InSpec DSL code. ### Syntax @@ -118,9 +118,9 @@ This subcommand has the following additional options: * `--client-key-pass=CLIENT_CERT_PASSWORD` Specify client certificate password, if required for SSL authentication (WinRM). * `--config=CONFIG` - Read configuration from JSON file (`-` reads from stdin). + Read the configuration from the JSON file (`-` reads from stdin). * `--docker-url` - Provides path to Docker API endpoint (Docker). + Provides a path to the Docker API endpoint (Docker). * `--enable-password=ENABLE_PASSWORD` Password for enable mode on Cisco IOS devices. * `--format=FORMAT` @@ -152,7 +152,7 @@ This subcommand has the following additional options: * `--ssl`, `--no-ssl` Use SSL for transport layer encryption (WinRM). * `--ssl-peer-fingerprint` - Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). + Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM). * `--sudo`, `--no-sudo` Run scans with sudo. Only activates on Unix and non-root user. * `--sudo-command=SUDO_COMMAND` @@ -174,7 +174,7 @@ This subcommand has the following additional options: * `--winrm-transport=WINRM_TRANSPORT` Specify which transport to use, defaults to negotiate (WinRM). * `--winrm-shell-type=WINRM_SHELL_TYPE` - Specify which shell type to use (powershell,elevated or cmd), defaults to powershell (WinRM). + Specify which shell type to use (PowerShell, elevated, or cmd), which defaults to PowerShell (WinRM). ## env @@ -192,7 +192,7 @@ inspec env Run all test files at the specified locations. -The subcommand loads the given profiles, fetches their dependencies if needed, then connects to the target and executes any controls contained in the profiles. One or more reporters are used to generate the output. +The subcommand loads the given profiles, fetches their dependencies if needed, then connects to the target and executes any controls in the profiles. One or more reporters are used to generate the output. ```ruby exit codes: @@ -314,13 +314,13 @@ This subcommand has the following additional options: * `--command-timeout=SECONDS` Maximum seconds to allow a command to run. * `--config=CONFIG` - Read configuration from JSON file (`-` reads from stdin). + Read the configuration from the JSON file (`-` reads from stdin). * `--controls=one two three` - A list of control names to run, or a list of regular expressions to match against control names. Ignore all other tests. + A list of control names to run or a list of /regexes/ to match against control names. Ignore all other tests. * `--create-lockfile`, `--no-create-lockfile` - Write out a lockfile based on this execution (unless one already exists). + Write out a lock file based on this execution (unless one already exists). * `--distinct-exit`, `--no-distinct-exit` - Exit with code 101 if any tests fail, and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. + Exit with code 101 if any tests fail and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. * `--docker-url` Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows. * `--enable-password=ENABLE_PASSWORD` @@ -328,7 +328,7 @@ This subcommand has the following additional options: * `--filter-empty-profiles`, `--no-filter-empty-profiles` Filter empty profiles (profiles without controls) from the report. * `--filter-waived-controls` - Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores `run` setting of waiver file. + Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores the `run` setting of the waiver file. * `--host=HOST` Specify a remote host which is tested. * `--input=name1=value1 name2=value2` @@ -352,13 +352,13 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: cli, documentation, html, progress, progress-bar, json, json-min, json-rspec, junit, yaml. + Enable one or more output reporters: cli, documentation, html, progress, progress-bar, json, json-min, json-rspec, jUnit, yaml. * `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` Include a code backtrace in report data (default: true). * `--reporter-include-source` Include full source code of controls in the CLI report. * `--reporter-message-truncation=REPORTER_MESSAGE_TRUNCATION` - Number of characters to truncate failure messages in report data to (default: no truncation). + Number of characters to truncate failure messages in report data (default: no truncation). * `--self-signed`, `--no-self-signed` Allow remote scans with self-signed certificates (WinRM). * `--shell`, `--no-shell` @@ -370,13 +370,13 @@ This subcommand has the following additional options: * `--show-progress`, `--no-show-progress` Show progress while executing tests. * `--silence-deprecations=all|GROUP GROUP...` - Suppress deprecation warnings. See install_dir/etc/deprecations.json for list of GROUPs or use 'all'. + Suppress deprecation warnings. See install_dir/etc/deprecations.json for a list of GROUPs or use 'all'. * `--ssh-config-file=one two three` A list of paths to the SSH configuration file, for example: `~/.ssh/config` or `/etc/ssh/ssh_config`. * `--ssl`, `--no-ssl` Use SSL for transport layer encryption (WinRM). * `--ssl-peer-fingerprint` - Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). + Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM). * `--sudo`, `--no-sudo` Run scans with sudo. Only activates on Unix and non-root user. * `--sudo-command=SUDO_COMMAND` @@ -388,9 +388,9 @@ This subcommand has the following additional options: * `-t`, `--target=TARGET` Simple targeting option using URIs, e.g. ssh://user:pass@host:port. * `--target-id=TARGET_ID` - Provide a ID which will be included on reports - deprecated. + Provide an ID that is included on reports - deprecated. * `--tags=one two three` - A list of tags or a list of regular expressions that match tags. `exec` will run controls referenced by the listed or matching tags. + A list of tags or regular expressions that match tags. `exec` will run controls referenced by the listed or matching tags. * `--user=USER` The login user for a remote scan. * `--vendor-cache=VENDOR_CACHE` @@ -444,7 +444,7 @@ inspec init TEMPLATE ## json -Read all tests in path and generate a json summary. +Read all tests in the path and generate a json summary. ### Syntax @@ -465,7 +465,7 @@ This subcommand has the following additional options: * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--tags=one two three` - A list of tags that reference certain controls. Other controls are ignored. + A list of tags that reference specific controls. Other controls are ignored. * `--vendor-cache=VENDOR_CACHE` Use the given path for caching dependencies. (default: `~/.inspec/cache`). @@ -549,11 +549,11 @@ This subcommand has the following additional options: * `--client-key-pass=CLIENT_CERT_PASSWORD` Specify client certificate password, if required for SSL authentication (WinRM). * `--config=CONFIG` - Read configuration from JSON file (`-` reads from stdin). + Read the configuration from the JSON file (`-` reads from stdin). * `--depends=one two three` A space-delimited list of local folders containing profiles whose libraries and resources will be loaded into the new shell. * `--distinct-exit`, `--no-distinct-exit` - Exit with code 100 if any tests fail, and 101 if any are skipped but none failed (default). If disabled, exit 0 on skips and 1 for failures. + Exit with code 100 if any tests fail and 101 if any are skipped, but none failed (default). If disabled, exit 0 on skips and 1 for failures. * `--docker-url` Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows. * `--enable-password=ENABLE_PASSWORD` @@ -577,7 +577,7 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit. + Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, jUnit. * `--self-signed`, `--no-self-signed` Allow remote scans with self-signed certificates (WinRM). * `--shell`, `--no-shell` @@ -591,7 +591,7 @@ This subcommand has the following additional options: * `--ssl`, `--no-ssl` Use SSL for transport layer encryption (WinRM). * `--ssl-peer-fingerprint=SSL_PEER_FINGERPRINT` - Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). + Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM). * `--sudo`, `--no-sudo` Run scans with sudo. Only activates on Unix and non-root user. * `--sudo-command=SUDO_COMMAND` @@ -636,7 +636,7 @@ This subcommand has additional options: ## vendor -Download all dependencies and generate a lockfile in a `vendor` directory. +Download all dependencies and generate a lock file in a `vendor` directory. ### Syntax @@ -651,7 +651,7 @@ inspec vendor PATH This subcommand has additional options: * `--overwrite`, `--no-overwrite` - Overwrite existing vendored dependencies and lockfile. + Overwrite existing vendored dependencies and lock files. ## version diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index ba63ee51c..0f9145e27 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -11,17 +11,17 @@ gh_repo = "inspec" weight = 50 +++ -Introduced in Chef InSpec 1.51.6 +A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. It is introduced in Chef InSpec 1.51.6. -A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. +Chef InSpec allows you to output your test results to one or more reporters. -Chef InSpec allows you to output your test results to one or more reporters. Configure the reporter(s) using either the `--reporter` option or as part of the general config file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). +Configure the reporter(s) using either the `--reporter` option or as part of the general config file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). ## Syntax -You can specify one or more reporters using the `--reporter` cli flag. You can also specify a output by appending a path separated by a colon. +You can specify one or more reporters using the `--reporter` CLI flag. You can also specify an output by appending a path separated by a colon. -Output json to screen. +**Output json to screen** ```bash inspec exec example_profile --reporter json @@ -29,7 +29,7 @@ inspec exec example_profile --reporter json inspec exec example_profile --reporter json:- ``` -Output yaml to screen +**Output yaml to screen.** ```bash inspec exec example_profile --reporter yaml @@ -37,33 +37,33 @@ inspec exec example_profile --reporter yaml inspec exec example_profile --reporter yaml:- ``` -Output cli to screen and write json to a file. +**Output cli to screen and write json to a file.** ```bash inspec exec example_profile --reporter cli json:/tmp/output.json ``` -Output nothing to screen and write junit and html to a file. +**Output nothing to screen and write jUnit and HTML to a file.** ```bash inspec exec example_profile --reporter junit2:/tmp/junit.xml html:www/index.html ``` -Output json to screen and write to a file. Write junit to a file. +**Output json to screen and write to a file. Write jUnit to a file.** ```bash inspec exec example_profile --reporter json junit2:/tmp/junit.xml | tee out.json ``` -If you wish to pass the profiles directly after specifying the reporters you will need to use the end of options flag `--`. +If you wish to pass the profiles directly after specifying the reporters, you must use the end of options flag `--`. ```bash inspec exec --reporter json junit2:/tmp/junit.xml -- profile1 profile2 ``` -If you are using the cli option `--config`, you can also set reporters. +Using the CLI option `--config`, you can also set reporters. -Output cli to screen. +**Output cli to screen.** ```json { @@ -75,7 +75,7 @@ Output cli to screen. } ``` -Output cli to screen and write json to a file. +**Output cli to screen and write json to a file.** ```json { @@ -91,24 +91,25 @@ Output cli to screen and write json to a file. } ``` -Output real-time progress to screen with a progress bar. +**Output real-time progress to screen with a progress bar.** + ```bash inspec exec example_profile --reporter progress-bar ``` ## Reporter Options -The following are CLI options that may be used to modify reporter behavior. Many of these options allow you to limit the size of the report, because some reporters (such as the json-automate reporter) have a limit on the total size of the report that can be processed. +The following are CLI options that are used to modify reporter behavior. Many of these options allow you to limit the report size because some reporters (such as the json-automate reporter) limit on the total size of the report that can be processed. `--diff`, `--no-diff` -: Include a `diff` comparison of textual differences in failed test output (default: `true`). +: Include a `diff` comparison of textual differences in the failed test output (default: `true`). -: Use `--no-diff` to limit the size of the report output when tests contain large amounts of text output. +: Use `--no-diff` to limit the size of the report output when tests contain large amounts of the text output. `--filter-empty-profiles` -: Remove empty profiles (those containing zero controls, such as resource packs) from the output of the reporter. +: Remove empty profiles (those containing zero controls, such as resource packs) from the reporter's output. `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` @@ -130,17 +131,17 @@ The following are CLI options that may be used to modify reporter behavior. Many : Includes enhanced outcome of controls in report data. -: The control level status outcomes are Passed, Failed, Not Applicable (N/A), Not Reviewed (N/R) or Error (ERR). +: The control level status outcomes are `Passed`, `Failed`, `Not Applicable (N/A)`, `Not Reviewed (N/R)`, or `Error (ERR)`. : Only supported for CLI, progress-bar, html2, json, json-automate, automate, and yaml reporters. ## Supported Reporters -The following are the current supported reporters: +The following are the currently supported reporters: -### cli +### CLI -This is the basic text base report. It includes details about which tests passed and failed and includes an overall summary at the end. +This is the basic text base report. It includes details about tests that passed and failed and an overall summary at the end. ### json @@ -174,43 +175,45 @@ This reporter includes all information about the profiles and test results in st This reporter is a very minimal text base report. It shows you which tests passed by name and has a small summary at the end. -### junit2 +### jUnit2 This reporter outputs the standard JUnit spec in XML format and is recommended for all new users of JUnit. -#### junit +#### jUnit -This legacy reporter outputs nonstandard JUnit XML and is provided only for backwards compatibility. +This legacy reporter outputs nonstandard JUnit XML and is provided only for backward compatibility. ### progress -This reporter is very condensed and gives you a `.`(pass), `f`(fail), or `*`(skip) character per test and a small summary at the end. +This reporter is very condensed and provides you a `.`(pass), `f`(fail), or `*`(skip) character per test and a small summary at the end. ### progress-bar -This reporter outputs real-time progress of a running InSpec profile using a progress bar and prints running control's ID with an indicator of control's status (Passed, failed or skipped). +This reporter outputs the real-time progress of a running InSpec profile using a progress bar and prints the running control's ID with an indicator of the control's status (`Passed`, `failed`, or `skipped`). For example: ![Progress Bar Reporter Outcome](/images/inspec/reporter_outcome_progress_bar.png) -And reporter outcome with `--enhanced-outcomes` option : +And reporter outcome with `--enhanced-outcomes` option: ![Progress Bar Reporter Outcome with enhanced outcomes](/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes.png) ### json-rspec -This reporter includes all information from the rspec runner. Unlike the json reporter this includes rspec specific details. +This reporter includes all information from the Rspec runner. Unlike the json reporter, this includes Rspec-specific details. -### html +### HTML -This reporter is the legacy RSpec HTML reporter, which is retained for backwards compatibility. The report generated is not aware of profiles or controls, and only contains unsorted test information. Most users should migrate to the `html2` reporter for more complete data. +This reporter is the legacy RSpec HTML reporter retained for backward compatibility. The report generated is unaware of profiles or controls and only contains unsorted test information. Most users should migrate to the `html2` reporter for more complete data. -### html2 +### HTML2 This reporter is an improved HTML reporter that contains full data about the structure of the profile, controls, and tests. The generated report renders HTML code for viewing your tests in a browser. -The `html2` reporter requires no configuration to function. However, two options--`alternate_css_file` and `alternate_js_file`--are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](/inspec/config/). +The `html2` reporter requires no configuration to function. However, options `--alternate_css_file` and `alternate_js_file--` are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. + +For details, see [our configuration file documentation](/inspec/config/). For example: @@ -228,15 +231,15 @@ For example: #### alternate_css_file -Specifies the full path to the location of a CSS file that will be read and inlined into the HTML report. The default CSS will not be included. +Specifies the full path to the location of a CSS file that is read and inlined into the HTML report. The default CSS is not included. #### alternate_js_file -Specifies the full path to the location of a JavaScript file that will be read and inlined into the HTML report. The default JavaScript will not be included. The JavaScript file should implement at least a `pageLoaded()` function, which will be called by the `onload` event of the HTML `body` element. +Specifies the full path to the location of a JavaScript file that is read and inlined into the HTML report. The default JavaScript is included. The JavaScript file should implement at least a `pageLoaded()` function, which is called by the `onload` event of the HTML `body` element. ## Automate Reporter -The `automate` reporter type is a special reporter which will send its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter you must pass in the correct configuration via a json config `--config`. +The `automate` reporter type is a special reporter which sends its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter, you must pass in the correct configuration via a json config `--config`. Example config: @@ -257,44 +260,34 @@ Example config: ### Mandatory fields -#### stdout +`stdout` +: Either suppress or shows the automate report in the CLI screen on completion. -This will either suppress or show the automate report in the CLI screen on completion +`url` +: Automate 2 url. Append `data-collector/v0/` at the end. -#### url - -This is your Automate 2 url. Append `data-collector/v0/` at the end. - -#### token - -This is your Automate 2 token. You can generate this token by navigating to the admin tab of A2 and then api keys. +`token` +: Automate 2 tokens. You can generate this token by navigating to the **admin** tab of A2 and then clicking **API keys**. ### Optional fields -#### insecure +`insecure` +: Disables or enables the SSL check when accessing the Automate 2 instance. -This will disable or enable the ssl check when accessing the Automate 2 instance. +`node_name` +: Node name which shows up in Automate. -#### node_name +`node_uuid` +: Node UUID, which shows up in Chef Automate. Use a single static UUID per node for all your reports. You must specify a `node_uuid` in the Chef InSpec configuration file if running Chef InSpec outside of an audit cookbook or another environment where a `chef_guid` or `node_uuid` is already known to Chef InSpec. -This will be the node name which shows up in Automate. - -#### node_uuid - -This will be the node UUID which shows up in Chef Automate. Use a single static UUID -per node for all your reports. You must specify a `node_uuid` in the Chef InSpec -configuration file if running Chef InSpec outside of an audit cookbook or another -environment where a `chef_guid` or `node_uuid` is already known to Chef InSpec. - -#### environment - -This will set the environment metadata for Automate. +`environment` +: Sets the environment metadata for Automate. ## JSON-Automate Reporter -The `json-automate` reporter is a special reporter that prepares the data format used by the Automate reporter. `json-automate` does not communicate on the network; rather it simply produces the JSON report format that Automate would be consuming. Notably, the report is based on the `json` reporter, with the following modifications: +The `json-automate` reporter is a special reporter that prepares the data format used by the Automate reporter. `json-automate` does not communicate on the network; instead, it simply produces the JSON report format that Automate would be consuming. Notably, the report is based on the `json` reporter, with the following modifications: - * Controls that appear in child profiles are de-duplicated by ID, merging up into the parent profile. - * Child profiles are deleted, flattening the report. +- Controls appearing in child profiles are de-duplicated by ID, merging into the parent profile. +- Child profiles are deleted, flattening the report. -The `json-automate` reporter is primarily used for internal needs, but some users may find it useful if they want a JSON based reporter that merges controls. +The `json-automate` reporter is primarily used for internal needs, but some users may find it helpful if they want a JSON-based reporter that merges controls. From 9811ef3b7c03e22074c8bf062880e2263595dfc0 Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Wed, 6 Jul 2022 15:00:09 +0530 Subject: [PATCH 79/93] Query Comments Addressed Signed-off-by: Deepa Kumaraswamy --- docs-chef-io/content/inspec/cli.md | 12 ++++++------ docs-chef-io/content/inspec/reporters.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 149a72f3e..40434b3c6 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -38,7 +38,7 @@ This subcommand has the following additional options: * `-o`, `--output=OUTPUT` Save the archive to a path. * `--overwrite`, `--no-overwrite` - Overwrite existing archives. + Overwrite existing archive. * `--profiles-path=PROFILES_PATH` Folder which contains referenced profiles. * `--tar`, `--no-tar` @@ -318,7 +318,7 @@ This subcommand has the following additional options: * `--controls=one two three` A list of control names to run or a list of /regexes/ to match against control names. Ignore all other tests. * `--create-lockfile`, `--no-create-lockfile` - Write out a lock file based on this execution (unless one already exists). + Write out a lockfile based on this execution (unless one already exists). * `--distinct-exit`, `--no-distinct-exit` Exit with code 101 if any tests fail and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. * `--docker-url` @@ -352,7 +352,7 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: cli, documentation, html, progress, progress-bar, json, json-min, json-rspec, jUnit, yaml. + Enable one or more output reporters: CLI, documentation, HTML, progress, progress-bar, json, json-min, json-rspec, jUnit, yaml. * `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` Include a code backtrace in report data (default: true). * `--reporter-include-source` @@ -577,7 +577,7 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, jUnit. + Enable one or more output reporters: CLI, documentation, HTML, progress, json, json-min, json-rspec, jUnit. * `--self-signed`, `--no-self-signed` Allow remote scans with self-signed certificates (WinRM). * `--shell`, `--no-shell` @@ -636,7 +636,7 @@ This subcommand has additional options: ## vendor -Download all dependencies and generate a lock file in a `vendor` directory. +Download all dependencies and generate a lockfile in a `vendor` directory. ### Syntax @@ -651,7 +651,7 @@ inspec vendor PATH This subcommand has additional options: * `--overwrite`, `--no-overwrite` - Overwrite existing vendored dependencies and lock files. + Overwrite existing vendored dependencies and lockfiles. ## version diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index 0f9145e27..69788a8dd 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -15,7 +15,7 @@ A `reporter` is a facility for formatting and delivering the results of a Chef I Chef InSpec allows you to output your test results to one or more reporters. -Configure the reporter(s) using either the `--reporter` option or as part of the general config file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). +Configure the reporter(s) using either the `--reporter` option or as part of the general configuration file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). ## Syntax @@ -239,9 +239,9 @@ Specifies the full path to the location of a JavaScript file that is read and in ## Automate Reporter -The `automate` reporter type is a special reporter which sends its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter, you must pass in the correct configuration via a json config `--config`. +The `automate` reporter type is a special reporter which sends its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter, you must pass in the correct configuration via a json configuration `--config`. -Example config: +Example Configuration: ```json { From 7cbaa892cc03b343ca560756d3f09ade71754d0e Mon Sep 17 00:00:00 2001 From: Clinton Wolfe Date: Thu, 7 Jul 2022 11:39:12 -0400 Subject: [PATCH 80/93] Corrections Signed-off-by: Clinton Wolfe --- docs-chef-io/content/inspec/cli.md | 6 +++--- docs-chef-io/content/inspec/reporters.md | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index 40434b3c6..b9826125c 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -174,7 +174,7 @@ This subcommand has the following additional options: * `--winrm-transport=WINRM_TRANSPORT` Specify which transport to use, defaults to negotiate (WinRM). * `--winrm-shell-type=WINRM_SHELL_TYPE` - Specify which shell type to use (PowerShell, elevated, or cmd), which defaults to PowerShell (WinRM). + Specify which shell type to use (powershell, elevated, or cmd), which defaults to powershell (WinRM). ## env @@ -352,7 +352,7 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: CLI, documentation, HTML, progress, progress-bar, json, json-min, json-rspec, jUnit, yaml. + Enable one or more output reporters: cli, documentation, html2, progress, progress-bar, json, json-min, json-rspec, junit2, yaml. * `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` Include a code backtrace in report data (default: true). * `--reporter-include-source` @@ -577,7 +577,7 @@ This subcommand has the following additional options: * `--proxy-command=PROXY_COMMAND` Specifies the command to use to connect to the server. * `--reporter=one two:/output/file/path` - Enable one or more output reporters: CLI, documentation, HTML, progress, json, json-min, json-rspec, jUnit. + Enable one or more output reporters: cli, documentation, html2, progress, json, json-min, json-rspec, junit2. * `--self-signed`, `--no-self-signed` Allow remote scans with self-signed certificates (WinRM). * `--shell`, `--no-shell` diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index 69788a8dd..6ffc432e9 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -11,7 +11,7 @@ gh_repo = "inspec" weight = 50 +++ -A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. It is introduced in Chef InSpec 1.51.6. +A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. Reporters were introduced in Chef InSpec 1.51.6. Chef InSpec allows you to output your test results to one or more reporters. @@ -105,7 +105,7 @@ The following are CLI options that are used to modify reporter behavior. Many of : Include a `diff` comparison of textual differences in the failed test output (default: `true`). -: Use `--no-diff` to limit the size of the report output when tests contain large amounts of the text output. +: Use `--no-diff` to limit the size of the report output when tests contain large amounts of text output. `--filter-empty-profiles` @@ -133,15 +133,15 @@ The following are CLI options that are used to modify reporter behavior. Many of : The control level status outcomes are `Passed`, `Failed`, `Not Applicable (N/A)`, `Not Reviewed (N/R)`, or `Error (ERR)`. -: Only supported for CLI, progress-bar, html2, json, json-automate, automate, and yaml reporters. +: Only supported for cli, progress-bar, html2, json, json-automate, automate, and yaml reporters. ## Supported Reporters The following are the currently supported reporters: -### CLI +### cli -This is the basic text base report. It includes details about tests that passed and failed and an overall summary at the end. +This is the basic text based report. It includes details about tests that passed and failed and an overall summary at the end. ### json @@ -175,11 +175,11 @@ This reporter includes all information about the profiles and test results in st This reporter is a very minimal text base report. It shows you which tests passed by name and has a small summary at the end. -### jUnit2 +### junit2 This reporter outputs the standard JUnit spec in XML format and is recommended for all new users of JUnit. -#### jUnit +#### junit This legacy reporter outputs nonstandard JUnit XML and is provided only for backward compatibility. @@ -203,15 +203,15 @@ And reporter outcome with `--enhanced-outcomes` option: This reporter includes all information from the Rspec runner. Unlike the json reporter, this includes Rspec-specific details. -### HTML +### html This reporter is the legacy RSpec HTML reporter retained for backward compatibility. The report generated is unaware of profiles or controls and only contains unsorted test information. Most users should migrate to the `html2` reporter for more complete data. -### HTML2 +### html2 This reporter is an improved HTML reporter that contains full data about the structure of the profile, controls, and tests. The generated report renders HTML code for viewing your tests in a browser. -The `html2` reporter requires no configuration to function. However, options `--alternate_css_file` and `alternate_js_file--` are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. +The `html2` reporter requires no configuration to function. However, options `--alternate_css_file` and `--alternate_js_file` are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](/inspec/config/). @@ -283,7 +283,7 @@ Example Configuration: `environment` : Sets the environment metadata for Automate. -## JSON-Automate Reporter +## json-Automate Reporter The `json-automate` reporter is a special reporter that prepares the data format used by the Automate reporter. `json-automate` does not communicate on the network; instead, it simply produces the JSON report format that Automate would be consuming. Notably, the report is based on the `json` reporter, with the following modifications: From 84c14a505ab415b8c4dc7deb4363dfea61974399 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Tue, 12 Jul 2022 16:31:32 +0530 Subject: [PATCH 81/93] Added impact setting option in only_if Signed-off-by: Nikita Mathur --- lib/inspec/rule.rb | 4 +++- .../enhanced-outcomes-test/controls/example.rb | 16 ++++++++++++++++ test/functional/inspec_exec_test.rb | 16 ++++++++++++---- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/inspec/rule.rb b/lib/inspec/rule.rb index 26b302b4c..0075080c0 100644 --- a/lib/inspec/rule.rb +++ b/lib/inspec/rule.rb @@ -16,6 +16,7 @@ module Inspec attr_reader :__waiver_data attr_accessor :resource_dsl attr_reader :__profile_id + attr_accessor :impact def initialize(id, profile_id, resource_dsl, opts, &block) @impact = nil @@ -133,10 +134,11 @@ module Inspec # # @param [Type] &block returns true if tests are added, false otherwise # @return [nil] - def only_if(message = nil) + def only_if(message = nil, impact: nil) return unless block_given? return if @__skip_only_if_eval == true + self.impact = impact if impact && !yield @__skip_rule[:result] ||= !yield @__skip_rule[:type] = :only_if @__skip_rule[:message] = message diff --git a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb index 79e7bf035..684fae88f 100644 --- a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb +++ b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb @@ -60,3 +60,19 @@ control "tmp-5.0" do it { should cmp "e.1" } end end + +# Example of setting impact using code and marking it N/A +control "tmp-6.0.1" do + only_if(impact: 0.0) { false } + describe file("/tmp") do + it { should be_directory } + end +end + +# Example of setting impact using code and not marked as N/A +control "tmp-6.0.2" do + only_if(impact: 0.5) { false } + describe file("/tmp") do + it { should be_directory } + end +end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 14860af2d..082ec71a5 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1340,14 +1340,14 @@ EOT end it "should show enhanced_outcomes for skipped tests in controls" do - _(run_result.stdout).must_include "3 skipped" - _(run_result.stdout).must_include "2 controls not reviewed" + _(run_result.stdout).must_include "5 skipped" + _(run_result.stdout).must_include "3 controls not reviewed" _(run_result.stdout).must_include "N/R" end it "should show enhanced_outcomes for controls with impact 0" do - _(run_result.stdout).must_include "3 skipped" - _(run_result.stdout).must_include "2 controls not applicable" + _(run_result.stdout).must_include "5 skipped" + _(run_result.stdout).must_include "3 controls not applicable" _(run_result.stdout).must_include "N/A" end @@ -1364,6 +1364,14 @@ EOT it "should show enhanced_outcomes for passed controls" do _(run_result.stdout).must_include "1 successful control" end + + it "should mark control as N/A using zero impact from only_if" do + _(run_result.stdout).must_include "N/A tmp-6.0.1" + end + + it "should not mark control as N/A using non-zeo impact from only_if" do + _(run_result.stdout).must_include "N/R tmp-6.0.2" + end end describe "when running profile with enhanced_outcomes option and yaml reporter" do From 1274a3969091aa294978f341ca262e3d0ffc1476 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Tue, 12 Jul 2022 17:10:55 +0530 Subject: [PATCH 82/93] Minitest deprecation warning fix for nil check Signed-off-by: Nikita Mathur --- test/functional/inspec_schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/inspec_schema_test.rb b/test/functional/inspec_schema_test.rb index 75b549bb9..d78c12c6e 100644 --- a/test/functional/inspec_schema_test.rb +++ b/test/functional/inspec_schema_test.rb @@ -26,7 +26,7 @@ describe "inspec schema" do json_output = JSON.parse(out.stdout) _(json_output["definitions"]["Control_Result"]["properties"]["resource_id"]).wont_be_nil # status value to be nil when not using enhanced outcomes flag - _(json_output["definitions"]["Exec_JSON_Control"]["properties"]["status"]).must_equal nil + assert_nil(json_output["definitions"]["Exec_JSON_Control"]["properties"]["status"]) end end From c8351c932382715ff4ec4105222562daad6eb307 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Wed, 13 Jul 2022 14:39:34 +0530 Subject: [PATCH 83/93] Added doc for only if impact change and modified test cases Signed-off-by: Nikita Mathur --- docs-chef-io/content/inspec/dsl_inspec.md | 20 +++++++++++++++++++ .../controls/example.rb | 3 ++- test/functional/inspec_exec_test.rb | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs-chef-io/content/inspec/dsl_inspec.md b/docs-chef-io/content/inspec/dsl_inspec.md index 99646e49b..77455d939 100644 --- a/docs-chef-io/content/inspec/dsl_inspec.md +++ b/docs-chef-io/content/inspec/dsl_inspec.md @@ -269,6 +269,25 @@ certain controls, which would 100% fail due to the way servers are prepared, but you know that the same control suites are reused later in different circumstances by different teams. +This example checks for if Gnome Desktop is installed or not, if not then it resets the impact of the control to the new value which is passed as a hash with impact key. Here it resets it to 0: +```ruby +control 'gnome-destkop-settings' do + impact 0.5 + desc 'some good settings' + desc 'check', 'check the settings file for good things' + desc 'fix', 'set the good things in the file /etc/gnome/settings' + tag nist: 'CM-6' + + only_if("The Gnome Desktop is not installed, this control is Not Applicable", impact: 0) { + package('gnome-desktop').installed? + } + + describe gnome_settings do + it should_be set_well + end +end +``` + Some notes about `only_if`: - `only_if` applies to the entire `control`. If the results of the `only_if` @@ -277,6 +296,7 @@ Some notes about `only_if`: blocks will not be run. However, bare Ruby expressions and bare Chef InSpec resources (not assocated with a describe block) preceding the only_if statement will run +- `only_if` also accepts hash with impact key to reset the impact value of the control. Control's impact is useful in determing it's enhanced outcome. To illustrate: diff --git a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb index 684fae88f..06918c7d6 100644 --- a/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb +++ b/test/fixtures/profiles/enhanced-outcomes-test/controls/example.rb @@ -63,7 +63,8 @@ end # Example of setting impact using code and marking it N/A control "tmp-6.0.1" do - only_if(impact: 0.0) { false } + impact 0.5 + only_if("Some reason for N/A", impact: 0.0) { false } describe file("/tmp") do it { should be_directory } end diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index 082ec71a5..ece2a9e17 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1367,6 +1367,7 @@ EOT it "should mark control as N/A using zero impact from only_if" do _(run_result.stdout).must_include "N/A tmp-6.0.1" + _(run_result.stdout).must_include "Some reason for N/A" end it "should not mark control as N/A using non-zeo impact from only_if" do From 25f08e162510b5fa4f91b2476c6d9d43b38b93a4 Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Wed, 13 Jul 2022 15:22:43 +0530 Subject: [PATCH 84/93] Minor edits Signed-off-by: Deepa Kumaraswamy --- docs-chef-io/content/inspec/dsl_inspec.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs-chef-io/content/inspec/dsl_inspec.md b/docs-chef-io/content/inspec/dsl_inspec.md index 77455d939..99a4ab882 100644 --- a/docs-chef-io/content/inspec/dsl_inspec.md +++ b/docs-chef-io/content/inspec/dsl_inspec.md @@ -249,6 +249,7 @@ end ``` This example checks for if certain pip packages are installed, but only if '/root/.aws' exists: + ```ruby control 'pip-packages-installed' do title 'Check if essential pips are installed' @@ -269,7 +270,10 @@ certain controls, which would 100% fail due to the way servers are prepared, but you know that the same control suites are reused later in different circumstances by different teams. -This example checks for if Gnome Desktop is installed or not, if not then it resets the impact of the control to the new value which is passed as a hash with impact key. Here it resets it to 0: +This example checks whether the Gnome Desktop is installed. If not installed, it resets the impact of the control to the new value which is passed as a hash with the impact key. + +Here, it resets it to 0: + ```ruby control 'gnome-destkop-settings' do impact 0.5 @@ -290,13 +294,13 @@ end Some notes about `only_if`: -- `only_if` applies to the entire `control`. If the results of the `only_if` +* `only_if` applies to the entire `control`. If the results of the `only_if` block evaluate to false, any Chef InSpec resources mentioned as part of a `describe` block will not be run. Additionally, the contents of the describe blocks will not be run. However, bare Ruby expressions and bare Chef InSpec resources (not assocated with a describe block) preceding the only_if statement will run -- `only_if` also accepts hash with impact key to reset the impact value of the control. Control's impact is useful in determing it's enhanced outcome. +* `only_if` also accepts hash with impact key to reset the impact value of the control. Control's impact is helpful in determining it is enhanced outcome. To illustrate: From 305ebb0231f4569a84c224f9a435d106cfa3e849 Mon Sep 17 00:00:00 2001 From: Deepa Kumaraswamy Date: Thu, 21 Jul 2022 16:54:13 +0530 Subject: [PATCH 85/93] Minor changes Signed-off-by: Deepa Kumaraswamy --- docs-chef-io/content/inspec/cli.md | 8 ++++---- docs-chef-io/content/inspec/reporters.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs-chef-io/content/inspec/cli.md b/docs-chef-io/content/inspec/cli.md index b9826125c..5202b202b 100644 --- a/docs-chef-io/content/inspec/cli.md +++ b/docs-chef-io/content/inspec/cli.md @@ -118,7 +118,7 @@ This subcommand has the following additional options: * `--client-key-pass=CLIENT_CERT_PASSWORD` Specify client certificate password, if required for SSL authentication (WinRM). * `--config=CONFIG` - Read the configuration from the JSON file (`-` reads from stdin). + Read configuration from the JSON file (`-` reads from stdin). * `--docker-url` Provides a path to the Docker API endpoint (Docker). * `--enable-password=ENABLE_PASSWORD` @@ -314,13 +314,13 @@ This subcommand has the following additional options: * `--command-timeout=SECONDS` Maximum seconds to allow a command to run. * `--config=CONFIG` - Read the configuration from the JSON file (`-` reads from stdin). + Read configuration from the JSON file (`-` reads from stdin). * `--controls=one two three` A list of control names to run or a list of /regexes/ to match against control names. Ignore all other tests. * `--create-lockfile`, `--no-create-lockfile` Write out a lockfile based on this execution (unless one already exists). * `--distinct-exit`, `--no-distinct-exit` - Exit with code 101 if any tests fail and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. + Exit with code 101 if any tests fail and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. * `--docker-url` Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows. * `--enable-password=ENABLE_PASSWORD` @@ -549,7 +549,7 @@ This subcommand has the following additional options: * `--client-key-pass=CLIENT_CERT_PASSWORD` Specify client certificate password, if required for SSL authentication (WinRM). * `--config=CONFIG` - Read the configuration from the JSON file (`-` reads from stdin). + Read configuration from the JSON file (`-` reads from stdin). * `--depends=one two three` A space-delimited list of local folders containing profiles whose libraries and resources will be loaded into the new shell. * `--distinct-exit`, `--no-distinct-exit` diff --git a/docs-chef-io/content/inspec/reporters.md b/docs-chef-io/content/inspec/reporters.md index 6ffc432e9..fbcd49e97 100644 --- a/docs-chef-io/content/inspec/reporters.md +++ b/docs-chef-io/content/inspec/reporters.md @@ -43,13 +43,13 @@ inspec exec example_profile --reporter yaml:- inspec exec example_profile --reporter cli json:/tmp/output.json ``` -**Output nothing to screen and write jUnit and HTML to a file.** +**Output nothing to screen and write junit and html to a file.** ```bash inspec exec example_profile --reporter junit2:/tmp/junit.xml html:www/index.html ``` -**Output json to screen and write to a file. Write jUnit to a file.** +**Output json to screen and write to a file. Write junit to a file.** ```bash inspec exec example_profile --reporter json junit2:/tmp/junit.xml | tee out.json From 742d21c6e29a9e347cfd3d7008dc2ee8abddca7f Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 18 Jul 2022 19:17:26 +0530 Subject: [PATCH 86/93] Duplicate method removal Signed-off-by: Nikita Mathur --- lib/inspec/rule.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/inspec/rule.rb b/lib/inspec/rule.rb index 0075080c0..96c3f6567 100644 --- a/lib/inspec/rule.rb +++ b/lib/inspec/rule.rb @@ -16,7 +16,6 @@ module Inspec attr_reader :__waiver_data attr_accessor :resource_dsl attr_reader :__profile_id - attr_accessor :impact def initialize(id, profile_id, resource_dsl, opts, &block) @impact = nil @@ -138,7 +137,7 @@ module Inspec return unless block_given? return if @__skip_only_if_eval == true - self.impact = impact if impact && !yield + self.impact(impact) if impact && !yield @__skip_rule[:result] ||= !yield @__skip_rule[:type] = :only_if @__skip_rule[:message] = message From 249f0aec9ade4681e314032b3b842d3a6178688b Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Fri, 29 Jul 2022 14:42:26 +0530 Subject: [PATCH 87/93] Enhanced outcomes build fix for windows Signed-off-by: Nikita Mathur --- test/functional/inspec_exec_test.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/functional/inspec_exec_test.rb b/test/functional/inspec_exec_test.rb index ece2a9e17..75cf378f1 100644 --- a/test/functional/inspec_exec_test.rb +++ b/test/functional/inspec_exec_test.rb @@ -1366,12 +1366,20 @@ EOT end it "should mark control as N/A using zero impact from only_if" do - _(run_result.stdout).must_include "N/A tmp-6.0.1" + if windows? + _(run_result.stdout).must_include "[N/A] tmp-6.0.1" + else + _(run_result.stdout).must_include "N/A tmp-6.0.1" + end _(run_result.stdout).must_include "Some reason for N/A" end it "should not mark control as N/A using non-zeo impact from only_if" do - _(run_result.stdout).must_include "N/R tmp-6.0.2" + if windows? + _(run_result.stdout).must_include "[N/R] tmp-6.0.2" + else + _(run_result.stdout).must_include "N/R tmp-6.0.2" + end end end From 03793862d23840bb6a69cb88434bfa3c351d2dfb Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Mon, 1 Aug 2022 13:15:08 +0000 Subject: [PATCH 88/93] Bump version to 5.19.0 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd88dbe53..be3eb1562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.18.17](https://github.com/inspec/inspec/tree/v5.18.17) (2022-07-27) + +## [v5.19.0](https://github.com/inspec/inspec/tree/v5.19.0) (2022-08-01) #### Merged Pull Requests -- CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) +- CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) ### Changes since 5.18.14 release #### Merged Pull Requests +- CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) - CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) - Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) - Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) diff --git a/VERSION b/VERSION index 6d8f9c9ed..a472b6a48 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.18.17 \ No newline at end of file +5.19.0 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index e0a737545..b19ec4b49 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.18.17".freeze + VERSION = "5.19.0".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 7f8b4aeed..973b452d4 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.18.17".freeze + VERSION = "5.19.0".freeze end From b7ddac9dcc4aa2bddfac940511d2fc47cae9b8c1 Mon Sep 17 00:00:00 2001 From: Nikita Mathur Date: Mon, 1 Aug 2022 18:49:35 +0530 Subject: [PATCH 89/93] CFINSPEC-240 Extended file format support for waivers (#6193) * Added separate waiver file reader and support for csv Signed-off-by: Nikita Mathur * Added support for json format waivers Signed-off-by: Nikita Mathur * Added support for xls and xlsx Signed-off-by: Nikita Mathur * Build issues and updated description of gems Signed-off-by: Nikita Mathur * Doc changes for waivers about supports Signed-off-by: Nikita Mathur * Fix added to check final options presense in config Signed-off-by: Nikita Mathur * Renamed variables from inputs to waivers Signed-off-by: Nikita Mathur * Validation changes with other small changes Signed-off-by: Nikita Mathur * Headers validation added for json and yaml * Linter issues resolved Signed-off-by: Nikita Mathur * Some refactoring and message change Signed-off-by: Nikita Mathur * exit code check removed from test cases since not req Signed-off-by: Nikita Mathur * Doc change for waiver support for excel by showing example Signed-off-by: Nikita Mathur Co-authored-by: Clinton Wolfe --- docs-chef-io/content/inspec/waivers.md | 50 +++++- .../images/inspec/waivers_file_excel.png | Bin 0 -> 15122 bytes inspec.gemspec | 4 + lib/inspec/exceptions.rb | 2 + lib/inspec/rule.rb | 16 +- lib/inspec/runner.rb | 8 +- lib/inspec/utils/convert.rb | 8 + lib/inspec/utils/waivers/csv_file_reader.rb | 34 ++++ lib/inspec/utils/waivers/excel_file_reader.rb | 39 ++++ lib/inspec/utils/waivers/json_file_reader.rb | 15 ++ lib/inspec/waiver_file_reader.rb | 66 +++++++ .../profiles/waivers/basic/files/waivers.csv | 8 + .../profiles/waivers/basic/files/waivers.json | 77 ++++++++ .../profiles/waivers/basic/files/waivers.xls | Bin 0 -> 26112 bytes .../profiles/waivers/basic/files/waivers.xlsx | Bin 0 -> 8108 bytes .../waivers/basic/files/wrong-headers.csv | 8 + .../waivers/basic/files/wrong-headers.json | 9 + .../waivers/basic/files/wrong-headers.xls | Bin 0 -> 25600 bytes .../waivers/basic/files/wrong-headers.xlsx | Bin 0 -> 8771 bytes .../waivers/basic/files/wrong-headers.yaml | 4 + test/functional/waivers_test.rb | 166 +++++++++++++++++- 21 files changed, 502 insertions(+), 12 deletions(-) create mode 100644 docs-chef-io/static/images/inspec/waivers_file_excel.png create mode 100644 lib/inspec/utils/waivers/csv_file_reader.rb create mode 100644 lib/inspec/utils/waivers/excel_file_reader.rb create mode 100644 lib/inspec/utils/waivers/json_file_reader.rb create mode 100644 lib/inspec/waiver_file_reader.rb create mode 100644 test/fixtures/profiles/waivers/basic/files/waivers.csv create mode 100644 test/fixtures/profiles/waivers/basic/files/waivers.json create mode 100644 test/fixtures/profiles/waivers/basic/files/waivers.xls create mode 100644 test/fixtures/profiles/waivers/basic/files/waivers.xlsx create mode 100644 test/fixtures/profiles/waivers/basic/files/wrong-headers.csv create mode 100644 test/fixtures/profiles/waivers/basic/files/wrong-headers.json create mode 100644 test/fixtures/profiles/waivers/basic/files/wrong-headers.xls create mode 100644 test/fixtures/profiles/waivers/basic/files/wrong-headers.xlsx create mode 100644 test/fixtures/profiles/waivers/basic/files/wrong-headers.yaml diff --git a/docs-chef-io/content/inspec/waivers.md b/docs-chef-io/content/inspec/waivers.md index ef9f90f29..ca97deac9 100644 --- a/docs-chef-io/content/inspec/waivers.md +++ b/docs-chef-io/content/inspec/waivers.md @@ -12,8 +12,7 @@ gh_repo = "inspec" +++ Waivers is a mechanism to mark controls as "waived" for various reasons, and to -control the running and/or reporting of those controls. It uses a YAML input file -that identifies: +control the running and/or reporting of those controls. A waiver file identifies: 1. which controls are waived 1. a description of why it is waived @@ -31,7 +30,7 @@ inspec exec path/to/profile --waiver-file waivers.yaml ## File Format -Waiver files are [input files](/inspec/inputs/) with a specific format: +Waiver files support YAML, JSON, CSV, XLSX & XLS format. ```yaml control_id: @@ -40,6 +39,18 @@ control_id: justification: "reason for waiving this control" ``` +OR + +```json +{ + "control_id": { + "expiration_date": "YYYY-MM-DD", + "run": false, + "justification": "reason for waiving this control" + } +} +``` + - `expiration_date` sets the day that the waiver file will expire in YYYY-MM-DD format. Waiver files expire at 00:00 at the local time of the system on the specified date. Waiver files without an expiration date are permanent. `expiration_date` is optional. - `run` is optional. If absent or true, the control will run and be reported, but failures in it won't make the overall run fail. If present and false, the control will not be run. You may use any of yes, no, true or false. To avoid confusion, it is good practice to explicitly specify whether the control should run. @@ -48,6 +59,8 @@ control_id: ### Examples: +Example in YAML: + ```yaml waiver_control_1_2_3: expiration_date: 2019-10-15 @@ -58,3 +71,34 @@ xccdf_org.cisecurity.benchmarks_rule_1.1.1.4_Ensure_mounting_of_hfs_filesystems_ justification: "This might be a bug in the test. @qateam" run: false ``` + +Example in JSON: + +```json +{ + "waiver_control_1_2_3": { + "expiration_date": "2019-10-15T00:00:00.000Z", + "justification": "Not needed until Q3. @secteam" + }, + "xccdf_org.cisecurity.benchmarks_rule_1.1.1.4_Ensure_mounting_of_hfs_filesystems_is_disabled": { + "expiration_date": "2020-03-01T00:00:00.000Z", + "justification": "This might be a bug in the test. @qateam", + "run": false + } +} +``` + +Example in CSV/XLSX/XLS: + +These file formats support the following fields in a file: + +* `control_id` + _Required_. +* `justification` + _Required_. +* `run` + _Optional_. +* `expiration_date` + _Optional_. + +![Waiver File Excel Example](/images/inspec/waivers_file_excel.png) \ No newline at end of file diff --git a/docs-chef-io/static/images/inspec/waivers_file_excel.png b/docs-chef-io/static/images/inspec/waivers_file_excel.png new file mode 100644 index 0000000000000000000000000000000000000000..17f85be14b5960334df2a94ff84a15529a651abb GIT binary patch literal 15122 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 literal 0 HcmV?d00001 diff --git a/inspec.gemspec b/inspec.gemspec index 890295ca4..fb6fb99e5 100644 --- a/inspec.gemspec +++ b/inspec.gemspec @@ -34,6 +34,10 @@ Gem::Specification.new do |spec| # progress bar streaming reporter plugin support spec.add_dependency "progress_bar", "~> 1.3.3" + # roo support for reading excel waiver files + spec.add_dependency "roo", "~> 2.9.0" + spec.add_dependency "roo-xls" # extension for roo to read xls files + # Used for Azure profile until integrated into train spec.add_dependency "faraday_middleware", ">= 0.12.2", "< 1.1" diff --git a/lib/inspec/exceptions.rb b/lib/inspec/exceptions.rb index 5782bfd30..6bf386d58 100644 --- a/lib/inspec/exceptions.rb +++ b/lib/inspec/exceptions.rb @@ -10,5 +10,7 @@ module Inspec class SecretsBackendNotFound < ArgumentError; end class ProfileValidationKeyNotFound < ArgumentError; end class ProfileSigningKeyNotFound < ArgumentError; end + class WaiversFileNotReadable < ArgumentError; end + class WaiversFileDoesNotExist < ArgumentError; end end end diff --git a/lib/inspec/rule.rb b/lib/inspec/rule.rb index 96c3f6567..326566448 100644 --- a/lib/inspec/rule.rb +++ b/lib/inspec/rule.rb @@ -8,6 +8,8 @@ require "inspec/impact" require "inspec/resource" require "inspec/resources/os" require "inspec/input_registry" +require "inspec/waiver_file_reader" +require "inspec/utils/convert" module Inspec class Rule @@ -338,17 +340,20 @@ module Inspec # only_if mechanism) # Double underscore: not intended to be called as part of the DSL def __apply_waivers - input_name = @__rule_id # TODO: control ID slugging - registry = Inspec::InputRegistry.instance - input = registry.inputs_by_profile.dig(__profile_id, input_name) - return unless input && input.has_value? && input.value.is_a?(Hash) + control_id = @__rule_id # TODO: control ID slugging + waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options) + + waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil? + + return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash) # An InSpec Input is a datastructure that tracks a profile parameter # over time. Its value can be set by many sources, and it keeps a # log of each "set" event so that when it is collapsed to a value, # it can determine the correct (highest priority) value. # Store in an instance variable for.. later reading??? - @__waiver_data = input.value + @__waiver_data = waiver_data_by_profile[control_id] + __waiver_data["skipped_due_to_waiver"] = false __waiver_data["message"] = "" @@ -377,6 +382,7 @@ module Inspec # expiration_date. We only care here if it has a "run" key and it # is false-like, since all non-skipped waiver operations are handled # during reporting phase. + __waiver_data["run"] = Converter.to_boolean(__waiver_data["run"]) if __waiver_data.key?("run") return unless __waiver_data.key?("run") && !__waiver_data["run"] # OK, apply a skip. diff --git a/lib/inspec/runner.rb b/lib/inspec/runner.rb index 292208f0f..f6c510a35 100644 --- a/lib/inspec/runner.rb +++ b/lib/inspec/runner.rb @@ -60,9 +60,11 @@ module Inspec end if @conf[:waiver_file] - waivers = @conf.delete(:waiver_file) - @conf[:input_file] ||= [] - @conf[:input_file].concat waivers + @conf[:waiver_file].each do |file| + unless File.file?(file) + raise Inspec::Exceptions::WaiversFileDoesNotExist, "Waiver file #{file} does not exist." + end + end end # About reading inputs: diff --git a/lib/inspec/utils/convert.rb b/lib/inspec/utils/convert.rb index 3ff7a9fbe..abb13b377 100644 --- a/lib/inspec/utils/convert.rb +++ b/lib/inspec/utils/convert.rb @@ -5,4 +5,12 @@ module Converter val = val.to_i if val =~ /^\d+$/ val end + + def self.to_boolean(value) + if ["true", "True", "TRUE", true, "yes", "y", "YES", "Y"].include? value + true + elsif ["false", "False", "FALSE", false, "no", "n", "NO", "N"].include? value + false + end + end end diff --git a/lib/inspec/utils/waivers/csv_file_reader.rb b/lib/inspec/utils/waivers/csv_file_reader.rb new file mode 100644 index 000000000..eda6f9f16 --- /dev/null +++ b/lib/inspec/utils/waivers/csv_file_reader.rb @@ -0,0 +1,34 @@ +require "csv" unless defined?(CSV) + +module Waivers + class CSVFileReader + def self.resolve(path) + return nil unless File.file?(path) + + @headers ||= [] + fetch_data(path) + end + + def self.fetch_data(path) + waiver_data_hash = {} + CSV.foreach(path, headers: true) do |row| + row_hash = row.to_hash + @headers = row_hash.keys if @headers.empty? + control_id = row_hash["control_id"] + # delete keys and values not required in final hash + row_hash.delete("control_id") + row_hash.delete_if { |k, v| k.nil? || v.nil? } + + waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank? + end + + waiver_data_hash + rescue CSV::MalformedCSVError => e + raise "Error reading InSpec waivers in CSV: #{e}" + end + + def self.headers + @headers + end + end +end \ No newline at end of file diff --git a/lib/inspec/utils/waivers/excel_file_reader.rb b/lib/inspec/utils/waivers/excel_file_reader.rb new file mode 100644 index 000000000..46de1fc87 --- /dev/null +++ b/lib/inspec/utils/waivers/excel_file_reader.rb @@ -0,0 +1,39 @@ +require "roo" +require "roo-xls" + +module Waivers + class ExcelFileReader + def self.resolve(path) + return nil unless File.file?(path) + + @headers ||= [] + fetch_data(path) + end + + def self.fetch_data(path) + waiver_data_hash = {} + file_extension = File.extname(path) == ".xlsx" ? :xlsx : :xls + excel_file = Roo::Spreadsheet.open(path, extension: file_extension) + excel_file.sheet(0).parse(headers: true).each_with_index do |row, index| + if index == 0 + @headers = row.keys + else + row_hash = row + control_id = row_hash["control_id"] + # delete keys and values not required in final hash + row_hash.delete("control_id") + row_hash.delete_if { |k, v| k.nil? || v.nil? } + end + + waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank? + end + waiver_data_hash + rescue Exception => e + raise "Error reading InSpec waivers in Excel: #{e}" + end + + def self.headers + @headers + end + end +end \ No newline at end of file diff --git a/lib/inspec/utils/waivers/json_file_reader.rb b/lib/inspec/utils/waivers/json_file_reader.rb new file mode 100644 index 000000000..2bdd269fb --- /dev/null +++ b/lib/inspec/utils/waivers/json_file_reader.rb @@ -0,0 +1,15 @@ +module Waivers + class JSONFileReader + def self.resolve(path) + return nil unless File.file?(path) + + fetch_data(path) + end + + def self.fetch_data(path) + JSON.parse(File.read(path)) + rescue JSON::ParserError => e + raise "Error reading InSpec waivers in JSON: #{e}" + end + end +end \ No newline at end of file diff --git a/lib/inspec/waiver_file_reader.rb b/lib/inspec/waiver_file_reader.rb new file mode 100644 index 000000000..87b1ba623 --- /dev/null +++ b/lib/inspec/waiver_file_reader.rb @@ -0,0 +1,66 @@ +require "inspec/secrets/yaml" +require "inspec/utils/waivers/csv_file_reader" +require "inspec/utils/waivers/json_file_reader" +require "inspec/utils/waivers/excel_file_reader" + +module Inspec + class WaiverFileReader + + def self.fetch_waivers_by_profile(profile_id, files) + read_waivers_from_file(profile_id, files) if @waivers_data.nil? || @waivers_data[profile_id].nil? + @waivers_data[profile_id] + end + + def self.read_waivers_from_file(profile_id, files) + @waivers_data ||= {} + output = {} + + files.each do |file_path| + file_extension = File.extname(file_path) + data = nil + if [".yaml", ".yml"].include? file_extension + data = Secrets::YAML.resolve(file_path) + data = data.inputs unless data.nil? + validate_json_yaml(data) + elsif file_extension == ".csv" + data = Waivers::CSVFileReader.resolve(file_path) + headers = Waivers::CSVFileReader.headers + validate_headers(headers) + elsif file_extension == ".json" + data = Waivers::JSONFileReader.resolve(file_path) + validate_json_yaml(data) + elsif [".xls", ".xlsx"].include? file_extension + data = Waivers::ExcelFileReader.resolve(file_path) + headers = Waivers::ExcelFileReader.headers + validate_headers(headers) + end + output.merge!(data) if !data.nil? && data.is_a?(Hash) + + if data.nil? + raise Inspec::Exceptions::WaiversFileNotReadable, + "Cannot find parser for waivers file '#{file_path}'. " \ + "Check to make sure file has the appropriate extension." + end + end + + @waivers_data[profile_id] = output + end + + def self.validate_headers(headers, json_yaml = false) + required_fields = json_yaml ? %w{justification} : %w{control_id justification} + all_fields = %w{control_id justification expiration_date run} + + Inspec::Log.warn "Missing column headers: #{(required_fields - headers)}" unless (required_fields - headers).empty? + Inspec::Log.warn "Invalid column header: Column can't be nil" if headers.include? nil + Inspec::Log.warn "Extra column headers: #{(headers - all_fields)}" unless (headers - all_fields).empty? + end + + def self.validate_json_yaml(data) + headers = [] + data.each_value do |value| + headers.push value.keys + end + validate_headers(headers.flatten.uniq, true) + end + end +end \ No newline at end of file diff --git a/test/fixtures/profiles/waivers/basic/files/waivers.csv b/test/fixtures/profiles/waivers/basic/files/waivers.csv new file mode 100644 index 000000000..d64f0b819 --- /dev/null +++ b/test/fixtures/profiles/waivers/basic/files/waivers.csv @@ -0,0 +1,8 @@ +control_id,justification,run,expiration_date,,, +03_waivered_no_expiry_ran_passes,Sound reasoning,TRUE,,,, +04_waivered_no_expiry_ran_fails,Unassailable thinking,TRUE,2077-11-10T00:00:00Z,,, +,,,,,, +05_waivered_no_expiry_not_ran,Sheer cleverness,FALSE,,,, +06_waivered_expiry_in_past_ran_passes,Necessity,TRUE,,,, +14_waivered_expiry_in_future_z_not_ran,Lack of imagination,FALSE,2077-11-10T00:00:00Z,,, +random contorl id with no data,,,,,,random data in csv! \ No newline at end of file diff --git a/test/fixtures/profiles/waivers/basic/files/waivers.json b/test/fixtures/profiles/waivers/basic/files/waivers.json new file mode 100644 index 000000000..a3d55a001 --- /dev/null +++ b/test/fixtures/profiles/waivers/basic/files/waivers.json @@ -0,0 +1,77 @@ +{ + "03_waivered_no_expiry_ran_passes": { + "justification": "Sound reasoning", + "run": true + }, + "04_waivered_no_expiry_ran_fails": { + "justification": "Unassailable thinking", + "run": true + }, + "05_waivered_no_expiry_not_ran": { + "justification": "Sheer cleverness", + "run": false + }, + "06_waivered_expiry_in_past_ran_passes": { + "expiration_date": "1977-06-01T00:00:00.000Z", + "justification": "Necessity", + "run": true + }, + "07_waivered_expiry_in_past_ran_fails": { + "expiration_date": "1977-06-01T00:00:00.000Z", + "justification": "Whimsy", + "run": true + }, + "08_waivered_expiry_in_past_not_ran": { + "expiration_date": "1977-06-01T00:00:00.000Z", + "justification": "Contrariness", + "run": false + }, + "09_waivered_expiry_in_future_ran_passes": { + "expiration_date": "2077-06-01T00:00:00.000Z", + "justification": "Handwaving", + "run": true + }, + "10_waivered_expiry_in_future_ran_fails": { + "expiration_date": "2077-06-01T00:00:00.000Z", + "justification": "Didn't feel like it", + "run": true + }, + "11_waivered_expiry_in_future_not_ran": { + "expiration_date": "2077-06-01T00:00:00.000Z", + "justification": "Lack of imagination", + "run": false + }, + "12_waivered_expiry_in_future_z_ran_passes": { + "expiration_date": "2077-11-10T00:00:00.000Z", + "justification": "Handwaving", + "run": true + }, + "13_waivered_expiry_in_future_z_ran_fails": { + "expiration_date": "2077-11-10T00:00:00.000Z", + "justification": "Didn't feel like it", + "run": true + }, + "14_waivered_expiry_in_future_z_not_ran": { + "expiration_date": "2077-11-10T00:00:00.000Z", + "justification": "Lack of imagination", + "run": false + }, + "15_waivered_expiry_in_future_string_ran_passes": { + "expiration_date": "2077-06-01", + "justification": "Handwaving", + "run": true + }, + "16_waivered_expiry_in_future_string_ran_fails": { + "expiration_date": "2077-06-01", + "justification": "Didn't feel like it", + "run": true + }, + "17_waivered_expiry_in_future_string_not_ran": { + "expiration_date": "2077-06-01", + "justification": "Lack of imagination", + "run": false + }, + "18_waivered_no_expiry_default_run": { + "justification": "Too lazy to specify run, which defaults to true" + } +} \ No newline at end of file diff --git a/test/fixtures/profiles/waivers/basic/files/waivers.xls b/test/fixtures/profiles/waivers/basic/files/waivers.xls new file mode 100644 index 0000000000000000000000000000000000000000..ae415ca102bb8130d9915537f482433b1eb52075 GIT binary patch literal 26112 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%0Ay$V;2uiyZ=>l;O)T=gZ%A; zefWjX@vQYZ1mS2Q^gZkiEV1b(M4;G>X%kde!d?EceviiKUb&xOprBKo>!ROwD$jkr zCwFnKG+5 zxny)nvzO!6Pjb?KD5!jrb*cG!Q(3fK@WERZ=WiX{cWC0Y4=b$BJwMU)S>BAOc0*P| z=J$7I-^ll$e0uNCcAI`#-u@r`_f6d4@A}HjF*RrBx4+Unds0K)nRdRRCog^Vv#Zqw zRo|h7&Ye>|A8z!#-Z@`HY<-7ipLY)Ps~RmiH0@aAAm1+0C$`=2dOVijur^&Nj)VKx8QdyR)azPBZNeMOjec~yCaSN7VeRf8rs z&pO_IZU>LpeIG2Tt~`FIrgG52VM%fI<@UB&QENWkHvdyaLI3!HgMSMR&WR1)a;|)0 zPrurf{r;Wew!dfR*~4b|2FK$&#jY>(A0!^SD|x)^;rE~4K7abBF%Pm%?df}D_6ws+ z<0d>b|;TH$v2#2N3jo3naE{I-AXDeAG2jQ1aO@95I_=vxtC8>aS2O1D`i{;}hRNq-z! zzcJYFPUh0Gx{mdOhQ1%a&bHCB=AM1pyc;WKrflxMXpKx9|NG9nIntnROFdn#helRq zdD`uMG;eUu_`|b~PTqdMrEcYV$CW{gV^>UHGi214RgW%@5{I~^{}DNJ^l_^t9&4Kd zvgTDcv`m$zz4_?3Yi{>mS+F9o&VNzhw;uI__H5Yr&1z3K)wss$6d%6}(rbR|Bddou zY*9w-kDkAAZ1WRu_odxe|IoN6b#QXiKRWH6QkM20=A{dP^HvokmxWxPU(@gXh97UP zJCkTTtZez=oNo>(rEcf1GzGs}^IF=x%{gf=PM>mKuVqYr^W`6}QXY6~?WvAgk>5n*)p-%hcJTYR&GJJ{}8m!@9Xnd_Fq9`0`wqJA*-5|Dm^nOFkDJA$9`lG#-4In>!7YUfbL zx3V6r`v4u4`?5gR{RUW_76C;8&=uf}7LF+D_)5lH9S_&SI#(@62UJzHb|BEUqFNQ5{w zLHqSXBat|PBL%|@7lsmWVYrsUg<~7= z97;>)M36%{F^5q6WS_|)9wnPl0%bIwKxhs3BG`}iF-!mvGfw*>q9BN^hagoE(1^py z9K{3a4Z!6DZ4P-*Z2%r(f;>R|z(Y*&Kz!810LlV%4l!UZMXUe=-rqw`K+!^)1E@hW z)4G@9S<#GastSqZy=TpV8v^x+kiar6xJo&b0@^Y5*HpVs62ehswBa9F!@rS+@AdI7l;MOOcDhslH=;XSy&?HP@`dCF$sZDI6`;xk;o1{Y z5Tsy8A&^2Lg+apqJJA9Oua@KA)4&+`UIagZk-}Gi_-8lp%fGf9uJAt+7|!TNQVE_3 zIYOPu5c*Er4%%&;o6fr_aC9{s3gG G<^O*UH^%+| literal 0 HcmV?d00001 diff --git a/test/fixtures/profiles/waivers/basic/files/waivers.xlsx b/test/fixtures/profiles/waivers/basic/files/waivers.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a5d05f8f66e17bef192001e819e277de138dba8f GIT binary patch literal 8108 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/profiles/waivers/basic/files/wrong-headers.csv b/test/fixtures/profiles/waivers/basic/files/wrong-headers.csv new file mode 100644 index 000000000..86c07c571 --- /dev/null +++ b/test/fixtures/profiles/waivers/basic/files/wrong-headers.csv @@ -0,0 +1,8 @@ +control_id_random,justification_random,run_random,expiration_date_random,,, +03_waivered_no_expiry_ran_passes,Sound reasoning,TRUE,,,, +04_waivered_no_expiry_ran_fails,Unassailable thinking,TRUE,2077-11-10T00:00:00Z,,, +,,,,,, +05_waivered_no_expiry_not_ran,Sheer cleverness,FALSE,,,, +06_waivered_expiry_in_past_ran_passes,Necessity,TRUE,,,, +14_waivered_expiry_in_future_z_not_ran,Lack of imagination,FALSE,2077-11-10T00:00:00Z,,, +random contorl id with no data,,,,,,random data in csv! \ No newline at end of file diff --git a/test/fixtures/profiles/waivers/basic/files/wrong-headers.json b/test/fixtures/profiles/waivers/basic/files/wrong-headers.json new file mode 100644 index 000000000..90fc0dff1 --- /dev/null +++ b/test/fixtures/profiles/waivers/basic/files/wrong-headers.json @@ -0,0 +1,9 @@ +{ + "03_waivered_no_expiry_ran_passes": { + "justification_random": "Sound reasoning", + "run_random": true, + "expiration_date_random": "1977-06-01T00:00:00.000Z" + }, + "04_waivered_no_expiry_ran_fails": { + } +} \ No newline at end of file diff --git a/test/fixtures/profiles/waivers/basic/files/wrong-headers.xls b/test/fixtures/profiles/waivers/basic/files/wrong-headers.xls new file mode 100644 index 0000000000000000000000000000000000000000..9b720a4e2a9ce7fa21241d51f6ff79ae779968f2 GIT binary patch literal 25600 zcmeHQ30zIv_uuz+s~gQF61oji8Z{Crg$$)+euh+TgHl{Ye)Q^*A+yZtJ!3@15JjeX zc@dK0#Y34Mzwq$T^WNkB*E*+L_nteb-~adfeE!2dKKtCW*V+5K)?RzgMpM>&`y`+sCvYhl1!gE8&KyXRJpU%*^B5CVJiMh8`m3Z6R069Jt6=alG4qCrOYW=XXelJZCZ$QwnX zNg^e+9g78}&!Rfzw2SlA62E}H6l0+F^qOUor#-{3mgx7@68*nYf5;QYJDt2U8@5I&hkCMy8&ChDZCG=Kx}R>YmYPs=bAnr^NbLa`*` zU@@d8aVD(L<`(xPJSlxh-bL-SXbffQ3wjf24C|W{dq}h{@s1C5-Hqb_Wo-xvweHEY zu(Pmm3bpBr*ORQN+$1_@Zf{P}eL`)zappnKc*++~Ty2sfNDA4~V%eg4!^n^p*PYmc zpJ)-+!HFR;5L$%x_7;gBr7QNQ%*0uSlvL@D}v(JjSvbWXC$%cCQztn&Yt z=`1k0=7UY^Qxyo59!@mf;cTb$FHJuphn^vazD*82OAh^j9Qr{ybb0b@l2blU4qYC; zyz5)LhoD)~u)YrMA;(VOj`wnKFh zy_y8Wr76rLsdm8>m&T##Mk2aK3!O(uS|=$w!;fi+$~WtWwjW&)y_!sg%g!PkO%c6W zPKJGE>**P_Hv&D=<4#*t9`I>Bi^he9O#NW2CQCntB6>AUW=a2MJ?M+*V(nVMH6#mA zLCr^ha;G$!m%_CtDH3!nz$RMY=T+beeZA-jp$+p5wsZw7(xwXN_uEtf1!+?SPFpLW zOKlSe?2|TCK%;F_1?-eIRX{UuQw1DfZK{9{rA-xZWVERQ4!t&2z_HY(3h4aWRKd2b z73|tt!M?2(9NJpJv#k~SN>sq{C+-ATDck`JuK{yR@E0Pl!K}dMHn|fVn4+1tG>CI+ zK!o0kL0nn`;_M;`!gPYJ0McS5Qq7ST-Km)?dN=ULLUDz$InaK*QTE$)q5Sq+OMqk( zHQ6A-%ZfleBtgc>kwn;#Ehfo2Mt8aah&t7sD7Clu966FyY;6WnZ)##{YN}8aPGPqr z0Mam=!WczEjE3zYQ7b zPG$m2gW$xt1w=|Z)UgWTM~X`)4Fc2kuY*(sC@sGO~4oGk^Iy!Qi(QI#CQ_+!~OI0L35>KYm>jXBWjFRuUkPk*yO9Zsh9( z1KU>UL@@}2=U)e@2v7{dYYm9Ai(-%-5+IO~trLvj6!M+T4x z_{S&l)Y~pp0E=i6bq+tWw*H+^6|eDW1G9kcxHf@o^q6d%rP-J(U_-|s6dP53u#!3MjI!d9~sPQ9Ty?P~^jRBL5t2CRQ3fN#A)1)+Yex&eSg={v4Og3)P zY#bD@!Em984VNEzzoAh!n+{Ai?$T_W6|kYFwUp8{_>qr(yDOWG5wdYrY-y&z#{r=rIx^YFurvz=Y{V_CBH)j!hh($q#AGAG(t0RhBW`IG0r&3Rlg-AM$wr2y z*(zWoZfO+(!e?7$v+2xaBg4`h6|fPvw2FY@JFjK4F=4WiVQDT3*oa$NML=2I3E6DA zARBkZmc|1%?yayiXP4Fc<@8NgCL0-+W~qRUxTQI}yg!;QyEWaIY-CuPwE{NcmgekI zUS2MnjVY6j3`?_9z((BCoLz*qk+RwFm~3QNnv()H;+E#@l6C2sY&K?0HZm;DRRJ4u zOLKPFU%6Tq8%^10fT$YGi}_+!+&+>$&oU9J z5MNksoYwpVR>egTSRq6`;x#TKHubnJEVscLzF$~eL{eN>VoXAOCigo@K(>*vJ)t*8 zMUW)eA{7QxuNaa5Q!rdd!Dlam;&PUV0WL354Acm`A%dH@p232`;yJEFPD3KMo~;8r zIN)uBUo<}=mgmQhi_7GuGUVzgYzJ~h!(=dm#KP3j53b_ipSV;q%||2^u1TpiOGW2I zQqeh)RCI1VEZW+psR%QI529c3pJ%`Nb>4H9AO4d1|`po z3yTj+Oc2cDjpipOW^(-{q1bLhT{4Iy!`49T`*^DV6F`u8p!3lnPBPd7E?$XxA3M9r zyfmO~usq|RkN}3-R}z=iS;!&&ptJ;rsp5*b2n(2ru(V;<3Pcb?_+isx;-h#FD=-9A z(FM&80fEr=V?fX-5RiTgfpF?DU9DE-WTu%l#?f>L} z3%z%pu7`#oxQjSA_&_GOz6@~M56S_jrv_{a+*KSLd?CXjs2IoqM}J87n1ou^v)d6j z=twd&er6JwLXu>cu??X;LqW4M!4|R8TZX?7*8N4DhA#*Ho5NohCP?6y0%GE0`O})l z|8Rydoxm^Q*2j1l{kTC*+ykgITVt~?5_un7;#LF2X%Y{RpN!xqB?`j8y)Yazs|z^h z5de%W6@X2lW1%A&%Adl^5>b1z;f+d@!GlSq5g3jxlCrW0up9xFE}$F)LN#fh1()ER zo5xFU94{jh68V|ja7heyI~XsBkqAN&XrIXQ;zG5^rQsrTi=(A;B9}(zL@tfat!K9< zeF!XS1tld?JwBJgv;#l~Q96+Bb8(lC9#W#uk|@NYDLgj1~@1tQO*t^%R$=nj*>@|;1BF=P&9LoFr5 zw-39MsVsbrvKU&m17G=qZH$WM^Ame*<8~t3#CXz%OTRza4O`21i`~O|E5VRi@Nm1x z;zA;%dxR^*i+;N(l#O`RVWRu|GDjY*-JCe=Fiw8RALHVQ$sKx+(C;EZpuwE59dfrU_?8y$v zGvW@eyE>x8Nq11=kiQo_wI7z9@5Al>V&Uc0mv=3=@Y7aHqw=5g_l^0hF`#O+Z!XU$ z&aT#_@rS;T1;wW7Crh?u?;CdHov*EBgNgs-oQEDkwTH%LeAl^CzpxygE$@rYowfE@ znl?0JTS(6QXEUP$^1W8C6_~EwQ}t(W_EonzR%xHpw67(N{H0Sv#=72*%l+;=RVg^y zb-dT>r=PZ1K3aRC$lHCy!C`*fMaM5>R=>>sG`QTz*WpTGj}H!eY%_LVn{a9LC*z~f zr|{iwy5&CLy=%WxzvQT9M(B+wp0WM2^L>BFczN&kgt)W!SLd%@;69~d#6_Xu)DPaz z4Q^GWPD}-j2w}o0sjr;d`+mIudUA(;#~mV}0)E_VM+ZEouemY3#7T4MZ;Oi;-Aox- zRnn>Z9LJosevZ3SNWFDxw7JPWlZJaim(|*x;im8Tt#QBCm%}SQ)o=KfXSD5rTFE0n z@2k=EbG*){E-YWXXJ2>j#N?SvcF(_eGW2Zl&E5U3^)ksBQsA^vUuRiu(Aki~zSC}& z>sAdLbk8;1GIig&S)q4KxBOsS=N@qDgHgyGkEw^(=U+X1<|FaT?mXb~j;ihV?mT_G$m9A$!a2)bTHik4wCqsr-JJ`P#3<#N%Xq!38QMKKk zL5`~bXxM4pc?YZW`zq52-hiwx%d`ut9Bf9uRT;d_sDStlPjUIXw@Y7_zVm;5eSFzJ zdTeQb=}}SW*)eaty6Ds{D;;n@^_Slk%(>T?KJYDfq|xId?~+UB&;0(P$K1*S%~|PQ zYuEhfe&Da28_GYHE$MX0S^w1dg%N+us7Ow?ob+yLMCnbN=c}HVMeYo)U8b9n;y73@ z*Q?8#a^3kUIaLoQSSO4+y)q}bz2BAj!`uoiUFW#k^i1E9VfFNZ#;zpOkH*Jt_5ao? zWsSL6{nDb)%G57q*T2^~;ni`MVAVmhJ2z8j7ZzTK&fnkoHJdi%%#n{)XKj~LCFm~$W}bdm3{N9FB`f)B^Jb=-H2 z7csao`^`6n)%)YGZ~WDI*%G^=YNH6Ft4YUS) zp6xUL%bzblB}6$+$@UWR&z%~}FMqYOx6{)H!aqJde3)AQ`9p)wZ=v6=k1t-?(dx_n z8=>d^`n*PG|5Afr_a^`KDsS$Q(BjXp^So34@uKqF)teX68;7PZo|_g_>~FVF#q*EH ztn=9ey$6tk!&+hdG+7rrD&w6+joc`$Be`D#f-%FhG(weP-=zfDyFBn4m0`a5O8@1#F>g{V{W_LESg8{@Y2uJKiD@>!-?gwyAO37@ z$vmxn-szf;=gi-H-Q1+S`_|KmuPw>~rmYCvX0}|vV}bvp%F+iN4Y(tpbnW$G;oPk$ zOLJr2s4Txz^q0oPO=oTdan_g>ZrkvVj65GuShBg%>G-2l9=jvbCr^mn>_2wS{imhI zU020SUHq|snc?@l^vWJQ{!3d;Ytwl58$-RhW_d5e#!dC|nRDqmzk-)7#J zan!e9sY=0;pt%Jbk|V8G>xS<8M9#+FJ*ze5`qp7OFJ^|bb+{)|bsb0S?$WL@jMMnd zA%C;{>6?p;UMGJ)>uPGp!pn8%`V~Y^syoLqUJzqEaJ! zTvRi1s2g%MIlKHuhUR>;VN+d4zu%bYQ!z&U((o;58SAFZ`RCYV&s9Zn8%B7p801zH zV4LUT9O}5?c+!@Ta>L2{pWQkWP#kIU+@@h+T4u4PMubaN>83>klaA%C+!Iwa)m2?1 zB|kQ7V|I%Ip#c0N$=?=`ZWL^M@i` zt-6y}gWtVha(-Ck(9>(}EiYBqdA`_F%QM%Xy(TmFb>8DB0Jv_pZQ?8g{Y(DC539vI zjj&K3aq9b(mm~UZ*n4kP2mXuq`pyfs*=C=g{x)yXM$4>)XXe*GuQsW)NloXKUa8sT zT(^vyJS#ZhP#^O*R(8Xu&HLNF!`^v!-9iVw=rrt(UZAh*5$j0jpnA`ZQHk%zM5;WV z{%xdFZdm3#vo4$i7q$&Pc_3!2TZndS-s?}kR|JErDq{C;95cEmF{)SW(m}@K^Pko% zKYMX{Solvdk&A;yH43gAvKQPB*}is+hEsB+w|`Bs&j6>~r&$xK4IkII9u$0BP;4_H+f1wzqM|z-Ch89OyKE`?_|Xvf%K$>qKG}kni)9IL)9wjPu#TJy)8$+e5G~Rj_a^A?A+FvGn>CJH;8b4 z&@1{tXRhaqjkC60oN@Z)YM&6J5gE08ryZTTaCL&ms}51?hFb?(S%z3U^x7iO?lEkA zcIEQXpVAKZ-o0k*6Z74B2mExVa!6xR%8s{LtOY+>7KiK3uOY^pg0~5ef9~BmBg)a# zX!4hl17_DQR<+($R#9h>bs&IqWnxiL>BHuxv)J~q@kZXP7K(`-GUm9!!zBTj)4Z12 zPM}Y*NguN!bK2BW(HQ2F;+?=$KNE>LhiO5&Eena7(J(QXM$m#$3rj-c(kL*A;Nhyc zH3UK%RWcb`UkyPEAz2U@Wr>9raD%J_+Db7RmczpI_qn6@8Wq&gpxq(pCd7yJ z9o)GH0T02if5{@MbUJmmg3^)H@yxr2*tK;t%5!fUvFn}iUP1*BSs)(^LO=|rT&Uxg zx9C^rd^W6bW;1j^OGKah;o=u{i~%V?JFtd^2ey&k)5c}v7Bjr|L8U?*szZ>58FAWY zO1A{Gga@5tK@Q8&IX0z1QBDPNuuw?8g_Qv;1NAOU@xhYV9v#4jK8DT9ERY2;??Bs^ z!LK3dP5Q%Tg96G5e1L)nBCLS^ULny|Hhe2db7?AqMWeVKA>kE$KnLTS8I^&wb%@CS z`tKKrU^Uq&k>r7ty2abyeF}=!&0EJMwmpv5IsvT$60DC=ZSa9#Z21sKC}T7veMrk7 zVQ_sA61?{$ry*g0TndS@M{ugRu?lx+$^57s=&iYru~kd`z_5sz_{4|s@z2-bJAaJxMLR_> z*2nb#e7}!>aRmRu2L43}d|!`$WCnwC{67PD@M{Li9FhejOGw=zSwX`4*6?cssV5{` zNOq9yA;I2#>i%aagXNURzpjB0`0N9}WZ}WbeE8Qh@B_ZK97aTy+L3}1pdInJc!upU z)R_#XeiO3Np$bZP0cHSotVgyW~R{7n%C2M-*oWFTztDcz Je$&kVzW}}b;ST@+ literal 0 HcmV?d00001 diff --git a/test/fixtures/profiles/waivers/basic/files/wrong-headers.xlsx b/test/fixtures/profiles/waivers/basic/files/wrong-headers.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..60a2f386ccc3c0ebe43b07590614bdc82d70e8ff GIT binary patch literal 8771 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 ZbuMbEq9S$|0Kh_gf)K&GnC|Dd{{w3GIq(1g literal 0 HcmV?d00001 diff --git a/test/fixtures/profiles/waivers/basic/files/wrong-headers.yaml b/test/fixtures/profiles/waivers/basic/files/wrong-headers.yaml new file mode 100644 index 000000000..f6748a16c --- /dev/null +++ b/test/fixtures/profiles/waivers/basic/files/wrong-headers.yaml @@ -0,0 +1,4 @@ +03_waivered_no_expiry_ran_passes: + justification_random: Sound reasoning + run_random: true + expiration_date_random: 2077-11-10T00:00:00Z \ No newline at end of file diff --git a/test/functional/waivers_test.rb b/test/functional/waivers_test.rb index 3a4faa0b6..a52d9eae0 100644 --- a/test/functional/waivers_test.rb +++ b/test/functional/waivers_test.rb @@ -8,7 +8,7 @@ describe "waivers" do let(:waivers_profiles_path) { "#{profile_path}/waivers" } let(:run_result) { run_inspec_process(cmd, json: true) } let(:controls_by_id) { run_result; @json.dig("profiles", 0, "controls").map { |c| [c["id"], c] }.to_h } - let(:cmd) { "exec #{waivers_profiles_path}/#{profile_name} --input-file #{waivers_profiles_path}/#{profile_name}/files/#{waiver_file}" } + let(:cmd) { "exec #{waivers_profiles_path}/#{profile_name} --waiver-file #{waivers_profiles_path}/#{profile_name}/files/#{waiver_file}" } attr_accessor :out @@ -115,6 +115,110 @@ describe "waivers" do end end + describe "a fully pre-slugged control file with csv format waiver file" do + let(:profile_name) { "basic" } + let(:waiver_file) { "waivers.csv" } + + # rubocop:disable Layout/AlignHash + { + "01_not_waivered_passes" => "passed", + "02_not_waivered_fails" => "failed", + "03_waivered_no_expiry_ran_passes" => "passed", + "04_waivered_no_expiry_ran_fails" => "failed", + "05_waivered_no_expiry_not_ran" => "skipped", + "06_waivered_expiry_in_past_ran_passes" => "passed", + "14_waivered_expiry_in_future_z_not_ran" => "skipped", + }.each do |control_id, expected| + it "has all of the expected outcomes #{control_id}" do + assert_test_outcome expected, control_id + + if control_id !~ /not_waivered/ + assert_waiver_annotation control_id + else + refute_waiver_annotation control_id + end + end + end + end + + describe "a fully pre-slugged control file with json format waiver file" do + let(:profile_name) { "basic" } + let(:waiver_file) { "waivers.json" } + + # rubocop:disable Layout/AlignHash + { + "01_not_waivered_passes" => "passed", + "02_not_waivered_fails" => "failed", + "03_waivered_no_expiry_ran_passes" => "passed", + "04_waivered_no_expiry_ran_fails" => "failed", + "05_waivered_no_expiry_not_ran" => "skipped", + "06_waivered_expiry_in_past_ran_passes" => "passed", + "14_waivered_expiry_in_future_z_not_ran" => "skipped", + }.each do |control_id, expected| + it "has all of the expected outcomes #{control_id}" do + assert_test_outcome expected, control_id + + if control_id !~ /not_waivered/ + assert_waiver_annotation control_id + else + refute_waiver_annotation control_id + end + end + end + end + + describe "a fully pre-slugged control file with XLSX format waiver file" do + let(:profile_name) { "basic" } + let(:waiver_file) { "waivers.xlsx" } + + # rubocop:disable Layout/AlignHash + { + "01_not_waivered_passes" => "passed", + "02_not_waivered_fails" => "failed", + "03_waivered_no_expiry_ran_passes" => "passed", + "04_waivered_no_expiry_ran_fails" => "failed", + "05_waivered_no_expiry_not_ran" => "skipped", + "06_waivered_expiry_in_past_ran_passes" => "passed", + "14_waivered_expiry_in_future_z_not_ran" => "skipped", + }.each do |control_id, expected| + it "has all of the expected outcomes #{control_id}" do + assert_test_outcome expected, control_id + + if control_id !~ /not_waivered/ + assert_waiver_annotation control_id + else + refute_waiver_annotation control_id + end + end + end + end + + describe "a fully pre-slugged control file with XLS format waiver file" do + let(:profile_name) { "basic" } + let(:waiver_file) { "waivers.xls" } + + # rubocop:disable Layout/AlignHash + { + "01_not_waivered_passes" => "passed", + "02_not_waivered_fails" => "failed", + "03_waivered_no_expiry_ran_passes" => "passed", + "04_waivered_no_expiry_ran_fails" => "failed", + "05_waivered_no_expiry_not_ran" => "skipped", + "06_waivered_expiry_in_past_ran_passes" => "passed", + "14_waivered_expiry_in_future_z_not_ran" => "skipped", + }.each do |control_id, expected| + it "has all of the expected outcomes #{control_id}" do + assert_test_outcome expected, control_id + + if control_id !~ /not_waivered/ + assert_waiver_annotation control_id + else + refute_waiver_annotation control_id + end + end + end + end + describe "with --filter-waived-controls flag" do it "can execute and not hit failures" do inspec("exec " + "#{waivers_profiles_path}/purely-broken-controls" + " --filter-waived-controls --waiver-file #{waivers_profiles_path}/purely-broken-controls/files/waivers.yml" + " --no-create-lockfile" + " --no-color") @@ -203,4 +307,64 @@ describe "waivers" do end end end + + describe "with a waiver file that does not exist" do + let(:profile_name) { "basic" } + let(:waiver_file) { "no_file.yaml" } + it "raise file does not exist standard error" do + result = run_result + assert_includes result.stderr, "no_file.yaml does not exist" + assert_equal 1, result.exit_status + end + end + + describe "with a waiver file with wrong headers" do + let(:profile_name) { "basic" } + + describe "using csv file" do + let(:waiver_file) { "wrong-headers.csv" } + it "raise warnings" do + result = run_result + assert_includes result.stderr, "Missing column headers: [\"control_id\", \"justification\"]" + assert_includes result.stderr, "Invalid column header: Column can't be nil" + assert_includes result.stderr, "Extra column headers: [\"control_id_random\", \"justification_random\", \"run_random\", \"expiration_date_random\", nil]" + end + end + + describe "using xlsx file" do + let(:waiver_file) { "wrong-headers.xlsx" } + it "raise warnings" do + result = run_result + assert_includes result.stderr, "Missing column headers: [\"control_id\", \"justification\"]" + assert_includes result.stderr, "Extra column headers: [\"control_id_random\", \"justification_random\", \"run_random\", \"expiration_date_random\"]" + end + end + + describe "using xls file" do + let(:waiver_file) { "wrong-headers.xls" } + it "raise warnings" do + result = run_result + assert_includes result.stderr, "Missing column headers: [\"control_id\", \"justification\"]" + assert_includes result.stderr, "Extra column headers: [\"control_id_random\", \"justification_random\", \"run_random\", \"expiration_date_random\"]" + end + end + + describe "using json file" do + let(:waiver_file) { "wrong-headers.json" } + it "raise warnings" do + result = run_result + assert_includes result.stderr, "Missing column headers: [\"justification\"]" + assert_includes result.stderr, "Extra column headers: [\"justification_random\", \"run_random\", \"expiration_date_random\"]" + end + end + + describe "using yaml file" do + let(:waiver_file) { "wrong-headers.yaml" } + it "raise warnings" do + result = run_result + assert_includes result.stderr, "Missing column headers: [\"justification\"]" + assert_includes result.stderr, "Extra column headers: [\"justification_random\", \"run_random\", \"expiration_date_random\"]" + end + end + end end From 183d09c534d233af07a68aae7d5515bbd140bb2c Mon Sep 17 00:00:00 2001 From: Vasundhara Jagdale Date: Thu, 4 Aug 2022 18:43:45 +0530 Subject: [PATCH 90/93] Adds podman resources. (#6183) * CFINSPEC-368 Adds podman resource. Adds PodmanContainerFilter to handle podman.containers plural resource Signed-off-by: Vasu1105 * Fix lint errors Signed-off-by: Vasu1105 * Adds style simple to the filter table fields to flatten the array Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to work with podman images plural resource Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to work with podman networks plural resource Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to work with podman pods plural resource Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to work with info and version parameter Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to work with podman volumes plural resource. Also updated the docs to add pods, networks and other resource parameters details. Signed-off-by: Vasu1105 * CFINSPEC-368 Updated podman resource to aspect object id to verify values of specified Podman object. Signed-off-by: Vasu1105 * Updated docs Signed-off-by: Vasu1105 * Updated column names to keep them same as the field names Signed-off-by: Vasu1105 * Adds style simple to commands column for podman containers plural resource Signed-off-by: Vasu1105 * CFINSPEC-360 Adds podman_container resource. Adds podman_object module Signed-off-by: Vasu1105 * Fixed the require path Signed-off-by: Vasu1105 * Fixed deprecation warning in unit test Signed-off-by: Vasu1105 * Fixed indentation issue in the docs. Signed-off-by: Vasu1105 * Renamed the methods names Signed-off-by: Vasu1105 * Adds skip resource test Signed-off-by: Vasu1105 * Updated podman.images to fetch only high level information as using query with low level information does not have required fields and represents the data in different way. Signed-off-by: Vasu1105 * added method to parse command which uses format option to fetch specific placeholders Signed-off-by: Vasu1105 * Update the podman,containers to fetch only high level information Signed-off-by: Vasu1105 * Update podman.networks to fetch only hight level information Signed-off-by: Vasu1105 * Removed style for where it is not required Signed-off-by: Vasu1105 * Lint fix and some code improvisation Signed-off-by: Vasu1105 * It turn out to be the docker object module can be utilized but still there is scope to rename that module to generic and not specific if going to use with other container management tools Signed-off-by: Vasu1105 * Missing file in earlier commit Signed-off-by: Vasu1105 * Content Review Signed-off-by: Deepa Kumaraswamy * CFINSPEC-361: Test for podman_image matchers and properties Signed-off-by: Sonu Saha * CFINSPEC-361: Add podman_image matchers and properties Signed-off-by: Sonu Saha * CFINSPEC-361: Documentation for podman_image matchers and properties Signed-off-by: Sonu Saha * CFINSPEC-361: Fix Rubocop lint issues Signed-off-by: Sonu Saha * CFINSPEC-361: Test for more properties Signed-off-by: Sonu Saha * CFINSPEC-361: Add more podman_image properties Signed-off-by: Sonu Saha * CFINSPEC-361: Documentation for podman_image properties Signed-off-by: Sonu Saha * CFINSPEC-361: Test for low-level information of image Signed-off-by: Sonu Saha * CFINSPEC-361: Add inspec_info property to test low-level info Signed-off-by: Sonu Saha * CFINSPEC-361: Documentation for inspec_info property Signed-off-by: Sonu Saha * CFINSPEC-361: Modify resource id to avoid dependency on other methods Signed-off-by: Sonu Saha * CFINSPEC-361: Decouple podman_image from podman plural resource Signed-off-by: Sonu Saha * CFINSPEC-361: Modify unit test and reqd fixtures Signed-off-by: Sonu Saha * CFINSPEC-361: Handle nil scenario of inspect_info Signed-off-by: Sonu Saha * CFINSPEC-361: Add missing property: id Signed-off-by: Sonu Saha * CFINSPEC-361: Update documentation Signed-off-by: Sonu Saha * CFINSPEC-361: Rename method and attribute name as suggested Signed-off-by: Sonu Saha * CFINSPEC-361: Add stderr message in exception Signed-off-by: Sonu Saha * CFINSPEC-361: Remove comment Signed-off-by: Sonu Saha * CFINSPEC-361: Move json_key mapping to a method Signed-off-by: Sonu Saha * Content Review Signed-off-by: Deepa Kumaraswamy * CFINSPEC-361: Remove exception and add nil Signed-off-by: Sonu Saha * CFINSPEC-351: failing tests for podman_network resource Signed-off-by: Vasu1105 * CFINSPEC-351 Adds podman_network resource properties Signed-off-by: Vasu1105 * Fixed typo in the test Signed-off-by: Vasu1105 * Refactored the code for podman_image to handle non exisiting image and for creating dynamic methods for properties Signed-off-by: Vasu1105 * Uncommented the image test Signed-off-by: Vasu1105 * Update the example for podman_network Signed-off-by: Vasu1105 * Missing test fixture file Signed-off-by: Vasu1105 * Adds doc for podman_network resource Signed-off-by: Vasu1105 * Doc Review Signed-off-by: Deepa Kumaraswamy * Adds module for podman to group all common methods Signed-off-by: Vasu1105 * Updated code to match the stderr string for non existing image and network. Also added the test for the same. Added code comments. Signed-off-by: Vasu1105 * TEST: Add failing test for podman_volume (CFINSPEC-351) Signed-off-by: Sonu Saha * RESOURCE: Add podman_volume properties & matcher (CFINSPEC-351) Signed-off-by: Sonu Saha * TEST: Add mock cmds and fixtures for test to pass (CFINSPEC-351) Signed-off-by: Sonu Saha * DOCS: Update documentation for podman_volume (CFINSPEC-351) Signed-off-by: Sonu Saha * TEST: Add failing test for podman_pod (CFINSPEC-351) Signed-off-by: Sonu Saha * RESOURCE: Add podman_pod properties & matcher (CFINSPEC-351) Signed-off-by: Sonu Saha * TEST: Add mock cmds and fixtures for test to pass (CFINSPEC-351) Signed-off-by: Sonu Saha * DOCS: Update documentation for podman_pod (CFINSPEC-351) Signed-off-by: Sonu Saha * DOCS: Update properties of podman_volume (CFINSPEC-351) Signed-off-by: Sonu Saha * LINT: Remove trailing whitespaces (CFINSPEC-351) Signed-off-by: Sonu Saha * REFACTOR: Extend existing fixture, remove new fixtures (CFINSPEC-351) Signed-off-by: Sonu Saha * Content Review Signed-off-by: Deepa Kumaraswamy Co-authored-by: Deepa Kumaraswamy Co-authored-by: Sonu Saha --- .../content/inspec/resources/podman.md | 218 +++++++++++ .../inspec/resources/podman_container.md | 149 ++++++++ .../content/inspec/resources/podman_image.md | 189 ++++++++++ .../inspec/resources/podman_network.md | 189 ++++++++++ .../content/inspec/resources/podman_pod.md | 210 +++++++++++ .../content/inspec/resources/podman_volume.md | 155 ++++++++ lib/inspec/resources/podman.rb | 353 ++++++++++++++++++ lib/inspec/resources/podman_container.rb | 84 +++++ lib/inspec/resources/podman_image.rb | 108 ++++++ lib/inspec/resources/podman_network.rb | 81 ++++ lib/inspec/resources/podman_pod.rb | 101 +++++ lib/inspec/resources/podman_volume.rb | 87 +++++ lib/inspec/utils/podman.rb | 24 ++ test/fixtures/cmd/podman-errors | 6 + test/fixtures/cmd/podman-images-a | 4 + test/fixtures/cmd/podman-info | 150 ++++++++ test/fixtures/cmd/podman-inspec | 10 + test/fixtures/cmd/podman-inspect-info | 1 + test/fixtures/cmd/podman-network | 1 + test/fixtures/cmd/podman-network-ls | 1 + test/fixtures/cmd/podman-pod-inspect | 1 + test/fixtures/cmd/podman-pod-ps | 29 ++ test/fixtures/cmd/podman-ps-a | 5 + test/fixtures/cmd/podman-version | 1 + test/fixtures/cmd/podman-volume-inspect | 1 + test/fixtures/cmd/podman-volume-ls | 18 + test/helpers/mock_loader.rb | 19 + test/unit/resources/podman_container_test.rb | 31 ++ test/unit/resources/podman_image_test.rb | 37 ++ test/unit/resources/podman_network_test.rb | 125 +++++++ test/unit/resources/podman_pod_test.rb | 50 +++ test/unit/resources/podman_test.rb | 163 ++++++++ test/unit/resources/podman_volume_test.rb | 40 ++ 33 files changed, 2641 insertions(+) create mode 100644 docs-chef-io/content/inspec/resources/podman.md create mode 100644 docs-chef-io/content/inspec/resources/podman_container.md create mode 100644 docs-chef-io/content/inspec/resources/podman_image.md create mode 100644 docs-chef-io/content/inspec/resources/podman_network.md create mode 100644 docs-chef-io/content/inspec/resources/podman_pod.md create mode 100644 docs-chef-io/content/inspec/resources/podman_volume.md create mode 100644 lib/inspec/resources/podman.rb create mode 100644 lib/inspec/resources/podman_container.rb create mode 100644 lib/inspec/resources/podman_image.rb create mode 100644 lib/inspec/resources/podman_network.rb create mode 100644 lib/inspec/resources/podman_pod.rb create mode 100644 lib/inspec/resources/podman_volume.rb create mode 100644 lib/inspec/utils/podman.rb create mode 100644 test/fixtures/cmd/podman-errors create mode 100644 test/fixtures/cmd/podman-images-a create mode 100644 test/fixtures/cmd/podman-info create mode 100644 test/fixtures/cmd/podman-inspec create mode 100644 test/fixtures/cmd/podman-inspect-info create mode 100644 test/fixtures/cmd/podman-network create mode 100644 test/fixtures/cmd/podman-network-ls create mode 100644 test/fixtures/cmd/podman-pod-inspect create mode 100644 test/fixtures/cmd/podman-pod-ps create mode 100644 test/fixtures/cmd/podman-ps-a create mode 100644 test/fixtures/cmd/podman-version create mode 100644 test/fixtures/cmd/podman-volume-inspect create mode 100644 test/fixtures/cmd/podman-volume-ls create mode 100644 test/unit/resources/podman_container_test.rb create mode 100644 test/unit/resources/podman_image_test.rb create mode 100644 test/unit/resources/podman_network_test.rb create mode 100644 test/unit/resources/podman_pod_test.rb create mode 100644 test/unit/resources/podman_test.rb create mode 100644 test/unit/resources/podman_volume_test.rb diff --git a/docs-chef-io/content/inspec/resources/podman.md b/docs-chef-io/content/inspec/resources/podman.md new file mode 100644 index 000000000..c45c67670 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman.md @@ -0,0 +1,218 @@ ++++ +title = "podman resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman" + identifier = "inspec/resources/os/podman.md podman resource" + parent = "inspec/resources/os" ++++ + +Use the `podman` Chef InSpec audit resource to test the configuration data for the Podman resources. + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman` resource block allows you to write a test for many `containers`. + +```ruby + describe podman.containers do + its('ids') { should include "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" } + its('images) { should include "docker.io/library/ubuntu:latest" } + end +``` + +Or, if you want to query a specific `container`: + +```ruby + describe podman.containers.where(id: "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7") do + its('status') { should include "Up 44 hours ago" } + end +``` + +> Where +> +> - `.where()` specifies a specific item and value to which the resource parameters are compared. +> - `commands`, `created_at`, `ids`, `images`, `names`, `status`, `image_ids`, `labels`, `mounts`, `networks`, `pods`, `ports`, `running_for`, and `sizes` are valid parameters for `containers`. + +The `podman` resource block also allows you to write a test for many `images`. + +```ruby + describe podman.images do + its('repositories') { should_not include 'docker.io/library/nginx' } + end +``` + +Or, if you want to query a specific `image`: + +```ruby + describe podman.images.where(id: "c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f") do + it { should exist } + end +``` + +> Where +> +> - `.where()` specifies a specific filter and expected value, against which parameters are compared. +> - `repositories`, `tags`, `sizes`, `digests`, `history`, `created_at`, `history`, and`created_since` are valid parameters for `images`. + +The `podman` resource block also allows you to write a test for many `networks`. + +```ruby + describe podman.networks do + its("names") { should include "podman" } + end +``` + +Or, if you want to query a specific `network`: + +```ruby + describe podman.networks.where(id: "c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f") do + it { should exist } + end +``` + +> Where +> +> - `.where()` specifies a specific filter and expected value, against which parameters are compared. +> - `ids`, `names`, `drivers`, `network_interfaces`, `created`, `subnets`, `ipv6_enabled`, `internal`, `dns_enabled`, `options`, `labels`, and `ipam_options` are valid parameters for `networks`. + +The `podman` resource block also allows you to write a test for many `pods`. + +```ruby + describe podman.pods do + its("names") { should include "cranky_allen" } + end +``` + +Or, if you want to query a specific `pod`: + +```ruby + describe podman.pods.where(id: "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc") do + it { should exist } + end +``` + +> Where +> +> - `.where()` may specify a specific filter and expected value, against which parameters are compared. +> - `ids`, `cgroups`, `containers`, `created`, `infraids`, `names`, `namespaces`, `networks`, `status`, and `labels` are valid parameters for `pods`. + +## Examples + +The following examples show how to use this Chef InSpec audit resource. + +### Returns all running containers + +```ruby + podman.containers.running?.ids.each do |id| + describe podman.object(id) do + its('State.Health.Status') { should eq 'healthy' } + end + end +``` + +## Resource Parameter Examples + +### containers + +`containers` returns information about containers as returned by [podman ps -a](https://docs.podman.io/en/latest/markdown/podman.1.html). + +```ruby + describe podman.containers do + its("ids") { should include "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" } + its("labels") { should include "maintainer" => "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e" } + its('names') { should include "sweet_mendeleev" } + its("images") { should include "docker.io/library/nginx:latest" } + end +``` + +### images + +`images` returns information about a Podman image as returned by [podman images -a](https://docs.podman.io/en/latest/markdown/podman-images.1.html). + +```ruby + describe podman.images do + its('ids') { should include 'sha256:c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f ' } + its('sizes') { should_not include '80.3 GB' } + its('repositories") { should include "docker.io/library/nginx"} + end +``` + +### pods + +`pods` returns information about pods as returned by [podman pod ps](https://docs.podman.io/en/latest/markdown/podman-pod-ps.1.html). + +```ruby + describe podman.pods do + its("ids") { should include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" } + its("containers") { should eq [{ "Id" => "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", "Names" => "95cadbb84df7-infra", "Status" => "running" } ]} + its("names") { should include "cranky_allen" } + end +``` + +### networks + +`networks` returns information about a Podman network as returned by [podman network ls](https://docs.podman.io/en/latest/markdown/podman-network-ls.1.html). + +```ruby + describe podman.networks do + its("names") { should include "podman" } + its("ids") { should include "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9" } + its("ipv6_enabled") { should eq [false] } + end +``` + +### volumes + +`volumes` returns information about a Podman volume as returned by [podman volume ls](https://docs.podman.io/en/latest/markdown/podman-volume-ls.1.html). + +```ruby + describe podman.volumes do + its('names') { should include 'ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a' } + its('drivers') { should include 'local' } + end +``` + +### info + +`info` returns the parsed result of [podman info](https://docs.podman.io/en/latest/markdown/podman-info.1.html). + +```ruby + describe podman.info do + its("host.os") { should eq "linux" } + end +``` + +### version + +`version` returns the parsed result of [podman version](https://docs.podman.io/en/latest/markdown/podman-version.1.html) + +```ruby + describe podman.version do + its("Client.Version") { should eq "4.1.0"} + its('Server.Version') { should eq '4.1.0'} + end +``` + +### object('id') + +`object` returns low-level information about Podman objects as returned by [podman inspect](https://docs.podman.io/en/latest/markdown/podman-inspect.1.html). + +```ruby + describe docker.object(id) do + its('State.Running') { should eq true } + end +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). diff --git a/docs-chef-io/content/inspec/resources/podman_container.md b/docs-chef-io/content/inspec/resources/podman_container.md new file mode 100644 index 000000000..334335c54 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman_container.md @@ -0,0 +1,149 @@ ++++ +title = "podman_container resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman_container" + identifier = "inspec/resources/os/podman_container.md podman_container resource" + parent = "inspec/resources/os" ++++ + +Use the `podman_container` Chef InSpec audit resource to test the ... + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman_container` Chef InSpec audit resource ... + +```ruby + describe podman_container("sweet_mendeleev") do + it { should exist } + it { should be_running } + its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" } + its("image") { should eq "docker.io/library/nginx:latest" } + its("labels") { should include "maintainer"=>"NGINX Docker Maintainers " } + its("ports") { should eq nil } + end +``` + +## Resource Parameter Examples + +### name + +The container name can be provided with the `name` resource parameter. + +```ruby + describe podman_container(name: 'an-echo-server') do + it { should exist } + it { should be_running } + end +``` + +### container ID + +Alternatively, you can pass the container ID. + +```ruby + describe podman_container(id: '71b5df59442b') do + it { should exist } + it { should be_running } + end +``` + +## Properties + +## Property Examples + +The following examples show how to use this Chef InSpec resource. + +### id + +The `id` property tests the container ID. + +```ruby + its('id') { should eq '71b5df59...442b' } +``` + +### image + +The `image` property tests the value of the container image. + +```ruby + its('image') { should eq 'docker.io/library/nginx:latest' } +``` + +### labels + +The `labels` property tests the value of container image labels. + +```ruby + its('labels') { should eq "maintainer" => "NGINX Docker Maintainers " } +``` + +### ports + +The `ports` property tests the value of the Podmans ports. + +```ruby + its('ports') { should eq '0.0.0.0:1234->1234/tcp' } +``` + +### command + +The `command` property tests the value of the container run command. + +```ruby + its('command') { should eq 'nc -ll -p 1234 -e /bin/cat' } +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). The specific matchers of this resource are: `exist` and `be_running`. + +### exist + +The `exist` matcher specifies if the container exists. + +```ruby + it { should exist } +``` + +### be_running + +The `be_running` matcher checks if the container is running. + +```ruby + it { should be_running } +``` + +## Examples + +The following examples show how to use this Chef InSpec audit resource. + +### Ensures container exists + +The below test passes if the container `sweet_mendeleev` exists as part of the Podman instances. + +```ruby + describe podman_container('sweet_mendeleev') do + it { should exist } + end +``` + +### Ensures container is in running status + +The below test passes if the container `sweet_mendeleev` exists as part of the Podman instances and the status is running. + +```ruby + describe podman_container('sweet_mendeleev') do + it { should be_running } + end +``` diff --git a/docs-chef-io/content/inspec/resources/podman_image.md b/docs-chef-io/content/inspec/resources/podman_image.md new file mode 100644 index 000000000..29be516d1 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman_image.md @@ -0,0 +1,189 @@ ++++ +title = "podman_image resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman_image" + identifier = "inspec/resources/os/podman_image.md podman_image resource" + parent = "inspec/resources/os" ++++ + +Use the `podman_image` Chef InSpec audit resource to test the properties of a container image on Podman. + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman_image` Chef InSpec audit resource aids in testing the properties of a container image on Podman. + +```ruby + describe podman_image("docker.io/library/busybox") do + it { should exist } + its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" } + its("repo_tags") { should include "docker.io/library/busybox:latest" } + its("size") { should eq 1636053 } + its("os") { should eq "linux" } + end +``` + +> where +> +> - `id`, `repo_tags`, `size`, and `os` are properties of this resource to fetch the respective value of the container image. +> - `exist` is a matcher of this resource. + +### Resource Parameter Examples + +- The resource allows you to pass an image name. If the tag is missing for an image, `latest` is assumed as default. + +```ruby + describe podman_image("docker.io/library/busybox") do + it { should exist } + end +``` + +- The resource allows you to pass the repository and tag values as separate values. + +```ruby + describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do + it { should exist } + end +``` + +- The resource allows you to pass with an image ID. + +```ruby + describe podman_image(id: "8847e9bf6df8") do + it { should exist } + end +``` + +## Properties + +### id + +The `id` property returns the full image ID. + +```ruby + its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" } +``` + +### repo_tags + +The `repo_tags` property tests the value of the repository name. + +```ruby + its("repo_tags") { should include "docker.io/library/busybox:latest" } +``` + +### size + +The `size` property tests the size of the image in bytes + +```ruby + its("size") { should eq 1636053 } +``` + +### digest + +The `digest` property tests the value of the image digest. + +```ruby + its("digest") { should eq "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83" } +``` + +### created_at + +The `created_at` property tests the time of the image creation. + +```ruby + its("created_at") { should eq "2022-06-08T00:39:28.175020858Z" } +``` + +### version + +The `version` property tests the version of the image. + +```ruby + its("version") { should eq "20.10.12" } +``` + +### names_history + +The `names_history` property tests the names history of the image. + +```ruby + its("names_history") { should include "docker.io/library/busybox:latest" } +``` + +### repo_digests + +The `repo_digests` tests the digest of the repository of the given image. + +```ruby + its("repo_digests") { should include "docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a" } +``` + +### architecture + +The `architecture` tests the architecture of the given image. + +```ruby + its("architecture") { should eq "arm64" } +``` + +### os + +The `os` property tests the operating system of the given image. + +```ruby + its("os") { should eq "linux" } +``` + +### virtual_size + +The `virtual_size` property tests the virtual size of the given image. + +```ruby + its("virtual_size") { should eq 1636053 } +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). + +### exist + +The `exist` matcher tests if the image is available on Podman. + +```ruby + it { should exist } +``` + +## Examples + +### Test if an image exists on Podman and verify the various image properties + +```ruby + describe podman_image("docker.io/library/busybox") do + it { should exist } + its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" } + its("repo_tags") { should include "docker.io/library/busybox:latest" } + its("size") { should eq 1636053 } + its("digest") { should eq "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83" } + its("created_at") { should eq "2022-06-08T00:39:28.175020858Z" } + its("version") { should eq "20.10.12" } + its("names_history") { should include "docker.io/library/busybox:latest" } + its("repo_digests") { should include "docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a" } + its("architecture") { should eq "arm64" } + its("os") { should eq "linux" } + its("virtual_size") { should eq 1636053 } + its("resource_id") { should eq "docker.io/library/busybox:latest" } + end +``` diff --git a/docs-chef-io/content/inspec/resources/podman_network.md b/docs-chef-io/content/inspec/resources/podman_network.md new file mode 100644 index 000000000..6d9bf9e44 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman_network.md @@ -0,0 +1,189 @@ ++++ +title = "podman_network resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman_network" + identifier = "inspec/resources/os/podman_network.md podman_network resource" + parent = "inspec/resources/os" ++++ + +Use the `podman_network` Chef InSpec audit resource to test the properties of existing Podman networks. + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman_network` Chef InSpec audit resource aids in testing the properties of a Podman network. + +```ruby + describe podman_network("minikube") do + it { should exist } + its("id") { should eq "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" } + its("name") { should eq "minikube" } + its("ipv6_enabled") { should eq false } + its("network_interface") { should eq "podman1" } + end +``` + +> where +> +> - `id`, `name`, `ipv6_enabled`, and `network_interface` are properties of this resource to fetch the respective value of the Podman network. +> - `exist` is a matcher of this resource. + +### Resource Parameter Examples + +- The resource allows you to pass a network name. + +```ruby + describe podman_network("minikube") do + it { should exist } + end +``` + +- The resource allows you to pass with a Network ID. + +```ruby + describe podman_network("3a7c94d937d5") do + it { should exist } + end +``` + +## Properties + +### id + +The `id` property returns the full Podman Network ID. + +```ruby + its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" } +``` + +### name + +The `name` property tests the value of the Podman network name. + +```ruby + its("name") { should eq "minikube" } +``` + +### ipv6_enabled + +The `ipv6_enabled` property tests whether ipv6 is enabled on the Podman network. + +```ruby + its("ipv6_enabled") { should eq true } +``` + +### network_interface + +The `network_interface` property tests the value of the network interface settings on the Podman network. + +```ruby + its("network_interface") { should eq "podman0" } +``` + +### created + +The `created` property tests the timestamp when the Podman network was created. + +```ruby + its("created") { should eq "2022-07-06T08:51:11.735432521+05:30" } +``` + +### subnets + +The `subnets` property tests the list of subnets on the Podman network. + +```ruby + its("subnets") { should inclue "gateway"=>"192.168.49.1", "subnet"=>"192.168.49.0/24" } +``` + +### dns_enabled + +The `dns_enabled` property tests whether the Podman network has DNS enabled. + +```ruby + its("dns_enabled") { should be false } +``` + +### internal + +The `internal` property tests whether the specified Podman network is internal. + +```ruby + its("internal") { should eq true } +``` + +### ipam_options + +The `ipam_options` property tests the IPAM options of the given Podman network. + +```ruby + its("ipam_options") { should eq "driver" => "host-local" } +``` + +### labels + +The `labels` property tests the labels set for the specified Podman network. + +```ruby + its("labels") { should eq "created_by.minikube.sigs.k8s.io"=>"true", "name.minikube.sigs.k8s.io"=>"minikube" } +``` + +### driver + +The `driver` property tests the value of the Podman network driver. + +```ruby + its("driver") { should eq "bridge" } +``` + +### options + +The `options` property tests the network options for the specified Podman network. + +```ruby + its("options") { should eq nil } +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). + +### exist + +The `exist` matcher tests if the specified network is available on Podman. + +```ruby + it { should exist } +``` + +## Examples + +### Tests if a given Podman network exists and verifies the various network properties + +```ruby + describe podman_network("minikube") do + it { should exist } + its("id") { should eq "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" } + its("name") { should eq "minikube" } + its("ipv6_enabled") { should eq false } + its("network_interface") { should eq "podman1" } + its("subnets") { should include "gateway"=>"192.168.49.1", "subnet"=>"192.168.49.0/24" } + its("dns_enabled") { should eq true } + its("internal") { should eq false } + its("created") { should eq "2022-07-06T08:51:11.735432521+05:30" } + its("ipam_options") { should eq "driver" => "host-local" } + its("labels") { should eq "created_by.minikube.sigs.k8s.io"=>"true", "name.minikube.sigs.k8s.io"=>"minikube" } + its("driver") { should eq "bridge" } + its("options") { should eq nil } + end +``` diff --git a/docs-chef-io/content/inspec/resources/podman_pod.md b/docs-chef-io/content/inspec/resources/podman_pod.md new file mode 100644 index 000000000..a2a62a731 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman_pod.md @@ -0,0 +1,210 @@ ++++ +title = "podman_pod resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman_pod" + identifier = "inspec/resources/os/podman_pod.md podman_pod resource" + parent = "inspec/resources/os" ++++ + +Use the `podman_pod` Chef InSpec audit resource to test the properties of a pod on Podman. + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman_pod` Chef InSpec audit resource aids in testing the properties of a pod on Podman. + +```ruby + describe podman_pod("nginx-frontend") do + it { should exist } + its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" } + its("name") { should eq "nginx-frontend" } + its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" } + its("create_command") { should include "new:nginx-frontend" } + its("state") { should eq "Running" } + end +``` + +> where +> +> - `'nginx-frontend'` is the name of the pod. Pod ID and Pod names are valid parameters accepted by `podman_pod`. +> - `'id'`, `'name'`, `'created_at'`, `'create_command'`, and `'state'`, are properties of this resource to fetch the respective value of the podman pod. +> - `exist` is a matcher of this resource. + +## Properties + +- Properties of the resources are: `'id'`, `'name'`, `'created_at'`, `'create_command'`, `'state'`, `'hostname'`, `'create_cgroup'`, `'cgroup_parent'`, `cgroup_path`, `'create_infra'`, `'infra_container_id'`, `'infra_config'`, `'shared_namespaces'`, `'num_containers'`, and `'containers'` + +### `id` + +The `id` property returns the id of the pod. + +```ruby + its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" } +``` + +### `name` + +The `name` property returns the name of the pod. + +```ruby + its("name") { should eq "nginx-frontend" } +``` + +### `created_at` + +The `created_at` property returns the creation date of the pod. + +```ruby + its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" } +``` + +### `create_command` + +The `create_command` property returns an array of commands used to create the pod. + +```ruby + its("create_command") { should include "new:nginx-frontend" } +``` + +### `state` + +The `state` property returns the state of the pod. + +```ruby + its("state") { should eq "Running" } +``` + +### `hostname` + +The `hostname` property returns the hostname of the pod. + +```ruby + its("hostname") { should eq "" } +``` + +### `create_cgroup` + +The `create_cgroup` property returns a boolean value for cgroup creation of the pod. + +```ruby + its("create_cgroup") { should eq true } +``` + +### `cgroup_parent` + +The `cgroup_parent` property returns the name of the cgroup parent of the pod. + +```ruby + its("cgroup_parent") { should eq "user.slice" } +``` + +### `cgroup_path` + +The `cgroup_path` property returns the path of the cgroup parent of the pod. + +```ruby + its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" } +``` + +### `create_infra` + +The `create_infra` property returns a boolean value for the pod infra creation. + +```ruby + its("create_infra") { should eq true } +``` + +### `infra_container_id` + +The `infra_container_id` property returns the infra container ID of the pod. + +```ruby + its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" } +``` + +### `infra_config` + +The `infra_config` property returns a hash of the infra configuration of the pod. + +```ruby + its("infra_config") { should include "DNSOption" } +``` + +### `shared_namespaces` + +The `shared_namespaces` property returns an array of shared namespaces of the pod. + +```ruby + its("shared_namespaces") { should include "ipc" } +``` + +### `num_containers` + +The `num_containers` property returns the number of containers in the pod. + +```ruby + its("num_containers") { should eq 2 } +``` + +### `containers` + +The `containers` property returns an array of hashes about the information of containers in the pod. + +```ruby + its("containers") { should_not be nil } +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). + +### exist + +The `exist` matcher tests if the pod is available on Podman. + +```ruby + it { should exist } +``` + +## Examples + +### Test if a pod exists on Podman and verifies pod properties + +```ruby + describe podman_pod("nginx-frontend") do + it { should exist } + its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" } + its("name") { should eq "nginx-frontend" } + its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" } + its("create_command") { should include "new:nginx-frontend" } + its("state") { should eq "Running" } + its("hostname") { should eq "" } + its("create_cgroup") { should eq true } + its("cgroup_parent") { should eq "user.slice" } + its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" } + its("create_infra") { should eq true } + its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" } + its("infra_config") { should include "DNSOption" } + its("shared_namespaces") { should include "ipc" } + its("num_containers") { should eq 2 } + its("containers") { should_not be nil } + end +``` + +### Test if a pod does not exist on Podman + +```ruby + describe podman_pod("non_existing_pod") do + it { should_not exist } + end +``` diff --git a/docs-chef-io/content/inspec/resources/podman_volume.md b/docs-chef-io/content/inspec/resources/podman_volume.md new file mode 100644 index 000000000..5e1d37f37 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/podman_volume.md @@ -0,0 +1,155 @@ ++++ +title = "podman_volume resource" +draft = false +gh_repo = "inspec" +platform = "unix" + +[menu] + [menu.inspec] + title = "podman_volume" + identifier = "inspec/resources/os/podman_volume.md podman_volume resource" + parent = "inspec/resources/os" ++++ + +Use the `podman_volume` Chef InSpec audit resource to test the properties of a volume on Podman. + +## Availability + +### Installation + +This resource is distributed with Chef InSpec and is automatically available for use. + +## Syntax + +A `podman_volume` Chef InSpec audit resource aids in testing the properties of a volume on Podman. + +```ruby + describe podman_volume("my_volume") do + it { should exist } + its("name") { should eq "my_volume" } + its("driver") { should eq "local" } + its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" } + its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" } + end +``` + +> where +> +> - `'name'`, `'driver'`, `'mountpoint'`, and `'created_at'` are properties of this resource to fetch the respective value of the podman volume. +> - `exist` is a matcher of this resource. + +## Properties + +- Properties of the resources: `name`, `driver`, `mountpoint`, `created_at`, `labels`, `scope`, `options`, `mount_count`, `needs_copy_up`, and `needs_chown`. + +### name + +The `name` property returns the name of the volume. + +```ruby + its("name") { should eq "my_volume" } +``` + +### driver + +The `driver` property returns the value for the volume's driver environment. + +```ruby + its("driver") { should eq "local" } +``` + +### mountpoint + +The `mountpoint` property returns the value for the volume's mount path. + +```ruby + its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" } +``` + +### created_at + +The `created_at` property returns the creation date of the volume. + +```ruby + its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" } +``` + +### labels + +The `labels` property returns the labels associated with the volume. + +```ruby + its("labels") { should eq({}) } +``` + +### scope + +The `scope` property returns the scope of the volume. + +```ruby + its("scope") { should eq "local" } +``` + +### options + +The `options` property returns the options associated with the volume. + +```ruby + its("options") { should eq({}) } +``` + +### mount_count + +The `mount_count` property returns the **MountCount** value from the volume's inspect information. + +```ruby + its("mount_count") { should eq 0 } +``` + +### needs_copy_up + +The `needs_copy_up` property returns the **NeedsCopyUp** value from the volume's inspect information. + +```ruby + its("needs_copy_up") { should eq true } +``` + +### needs_chown + +The `needs_chown` property returns the **NeedsChown** value from the volume's inspect information. + +```ruby + its("needs_chown") { should eq true } +``` + +## Matchers + +For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). + +### exist + +The `exist` matcher tests if the volume is available on Podman. + +```ruby + it { should exist } +``` + +## Examples + +### Test if a volume exists on Podman and verifies volume properties + +```ruby + describe podman_volume("my_volume") do + it { should exist } + its("name") { should eq "my_volume" } + its("driver") { should eq "local" } + its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" } + its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" } + its("labels") { should eq({}) } + its("scope") { should eq "local" } + its("options") { should eq({}) } + its("mount_count") { should eq 0 } + its("needs_copy_up") { should eq true } + its("needs_chown") { should eq true } + end +``` diff --git a/lib/inspec/resources/podman.rb b/lib/inspec/resources/podman.rb new file mode 100644 index 000000000..90987ada9 --- /dev/null +++ b/lib/inspec/resources/podman.rb @@ -0,0 +1,353 @@ +require "inspec/resources/command" +require "inspec/utils/filter" +require "hashie/mash" + +module Inspec::Resources + class Podman < Inspec.resource(1) + # Resource requires an internal name. + name "podman" + + # Restrict to only run on the below platforms (if none were given, + # all OS's and cloud API's supported) + supports platform: "unix" + + desc "A resource to retrieve information about podman" + + example <<~EXAMPLE + describe podman.containers do + its('images') { should include "docker.io/library/ubuntu:latest" } + end + + describe podman.images do + its('names') { should_not include "docker.io/library/ubuntu:latest" } + end + + describe podman.pods do + its("ids") { should include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" } + end + + describe podman.info.host do + its("os") { should eq "linux"} + end + + describe podman.version do + its("Client.Version") { should eq "4.1.0"} + end + + podman.containers.ids.each do |id| + # call podman inspect for a specific container id + describe podman.object(id) do + its("State.OciVersion") { should eq "1.0.2-dev" } + its("State.Running") { should eq true} + end + end + EXAMPLE + + def containers + PodmanContainerFilter.new(parse_containers) + end + + def images + PodmanImageFilter.new(parse_images) + end + + def networks + PodmanNetworkFilter.new(parse_networks) + end + + def pods + PodmanPodFilter.new(parse_pods) + end + + def volumes + PodmanVolumeFilter.new(parse_volumes) + end + + def version + return @version if defined?(@version) + + sub_cmd = "version --format json" + output = run_command(sub_cmd) + @version = Hashie::Mash.new(JSON.parse(output)) + rescue JSON::ParserError => _e + Hashie::Mash.new({}) + end + + def info + return @info if defined?(@info) + + sub_cmd = "info --format json" + output = run_command(sub_cmd) + @info = Hashie::Mash.new(JSON.parse(output)) + rescue JSON::ParserError => _e + Hashie::Mash.new({}) + end + + # returns information about podman objects + def object(id) + return @inspect if defined?(@inspect) + + output = run_command("inspect #{id} --format json") + data = JSON.parse(output) + data = data[0] if data.is_a?(Array) + @inspect = Hashie::Mash.new(data) + rescue JSON::ParserError => _e + Hashie::Mash.new({}) + end + + def to_s + "Podman" + end + + private + + # Calls the run_command method to get all podman containers and parse the command output. + # Returns the parsed command output. + def parse_containers + labels = %w{ID Image ImageID Command CreatedAt RunningFor Status Pod Ports Size Names Networks Labels Mounts} + parse_json_command(labels, "ps -a --no-trunc --size") + end + + # Calls the run_command method to get all podman images and parse the command output. + # Returns the parsed command output. + def parse_images + labels = %w{ID Repository Tag Size Digest CreatedAt CreatedSince History} + parse_json_command(labels, "images -a --no-trunc") + end + + # Calls the run_command method to get all podman network list and parse the command output. + # Returns the parsed command output. + def parse_networks + labels = %w{ID Name Driver Labels Options IPAMOptions Created Internal IPv6Enabled DNSEnabled NetworkInterface Subnets} + parse_json_command(labels, "network ls --no-trunc") + end + + # Calls the run_command method to get all podman pod list and parse the command output. + # Returns the parsed command output. + def parse_pods + sub_cmd = "pod ps --no-trunc --format json" + output = run_command(sub_cmd) + parse(output) + end + + # Calls the run_command method to get all podman volume list and parse the command output. + # Returns the parsed command output. + def parse_volumes + sub_cmd = "volume ls --format json" + output = run_command(sub_cmd) + parse(output) + end + + # Runs the given podman command on the host machine on which podman is installed + # Returns the command output or raises the command execution error. + def run_command(subcommand) + result = inspec.command("podman #{subcommand}") + if result.stderr.empty? + result.stdout + else + raise "Error while running command \'podman #{subcommand}\' : #{result.stderr}" + end + end + + def parse_json_command(labels, subcommand) + # build command + format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" } + raw = inspec.command("podman #{subcommand} --format '{#{format.join(", ")}}'").stdout + output = [] + + raw.each_line do |entry| + # convert all keys to lower_case to work well with ruby and filter table + row = JSON.parse(entry).map do |key, value| + [key.downcase, value] + end.to_h + + # ensure all keys are there + row = ensure_keys(row, labels) + output.push(row) + end + + output + rescue JSON::ParserError => _e + warn "Could not parse `podman #{subcommand}` output" + [] + end + + def ensure_keys(entry, labels) + labels.each do |key| + entry[key.downcase] = nil unless entry.key?(key.downcase) + end + entry + end + + # Method to parse JDON content. + # Returns: Parsed data. + def parse(content) + require "json" unless defined?(JSON) + output = JSON.parse(content) + parsed_output = [] + output.each do |entry| + entry = entry.map do |k, v| + [k.downcase, v] + end.to_h + parsed_output << entry + end + parsed_output + rescue => e + raise Inspec::Exceptions::ResourceFailed, "Unable to parse command JSON output: #{e.message}" + end + end + + # class for podman.containers plural resource + class PodmanContainerFilter + filter = FilterTable.create + filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } + filter.register_column(:commands, field: "command") + .register_column(:ids, field: "id") + .register_column(:created_at, field: "createdat") + .register_column(:images, field: "image") + .register_column(:names, field: "names") + .register_column(:status, field: "status") + .register_column(:image_ids, field: "image_id") + .register_column(:labels, field: "labels", style: :simple) + .register_column(:mounts, field: "mounts") + .register_column(:networks, field: "networks") + .register_column(:pods, field: "pod") + .register_column(:ports, field: "ports") + .register_column(:sizes, field: "size") + .register_column(:running_for, field: "running_for") + .register_custom_matcher(:running?) do |x| + x.where { status.downcase.start_with?("up") } + end + filter.install_filter_methods_on_resource(self, :containers) + + attr_reader :containers + def initialize(containers) + @containers = containers + end + + def to_s + "Podman Containers" + end + + def resource_id + "Podman Containers" + end + end + + # class for podman.images plural resource + class PodmanImageFilter + filter = FilterTable.create + filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } + filter.register_column(:ids, field: "id") + .register_column(:repositories, field: "repository") + .register_column(:tags, field: "tag") + .register_column(:sizes, field: "size") + .register_column(:digests, field: "digest") + .register_column(:created_at, field: "createdat") + .register_column(:created_since, field: "createdsince") + .register_column(:history, field: "history") + filter.install_filter_methods_on_resource(self, :images) + + attr_reader :images + def initialize(images) + @images = images + end + + def to_s + "Podman Images" + end + + def resource_id + "Podman Images" + end + end + + class PodmanNetworkFilter + filter = FilterTable.create + filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } + .register_column(:ids, field: "id") + .register_column(:names, field: "name") + .register_column(:drivers, field: "driver") + .register_column(:network_interfaces, field: "networkinterface") + .register_column(:created, field: "created") + .register_column(:subnets, field: "subnets") + .register_column(:ipv6_enabled, field: "ipv6enabled") + .register_column(:internal, field: "internal") + .register_column(:dns_enabled, field: "dnsenabled") + .register_column(:ipam_options, field: "ipamoptions") + .register_column(:options, field: "options") + .register_column(:labels, field: "labels") + filter.install_filter_methods_on_resource(self, :networks) + + attr_reader :networks + def initialize(networks) + @networks = networks + end + + def to_s + "Podman Networks" + end + + def resource_id + "Podman Networks" + end + end + + class PodmanPodFilter + filter = FilterTable.create + filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } + .register_column(:ids, field: "id") + .register_column(:cgroups, field: "cgroup") + .register_column(:containers, field: "containers") + .register_column(:created, field: "created") + .register_column(:infraids, field: "infraid") + .register_column(:names, field: "name") + .register_column(:namespaces, field: "namespace") + .register_column(:networks, field: "networks") + .register_column(:status, field: "status") + .register_column(:labels, field: "labels") + filter.install_filter_methods_on_resource(self, :pods) + + attr_reader :pods + def initialize(pods) + @pods = pods + end + + def to_s + "Podman Pods" + end + + def resource_id + "Podman Pods" + end + end + + class PodmanVolumeFilter + filter = FilterTable.create + filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? } + .register_column(:names, field: "name") + .register_column(:drivers, field: "driver") + .register_column(:mountpoints, field: "mountpoint") + .register_column(:createdat, field: "createdat") + .register_column(:labels, field: "labels") + .register_column(:scopes, field: "scope") + .register_column(:options, field: "options") + .register_column(:mountcount, field: "mountcount") + .register_column(:needscopyup, field: "needscopyup") + .register_column(:needschown, field: "needschown") + filter.install_filter_methods_on_resource(self, :volumes) + + attr_reader :volumes + def initialize(volumes) + @volumes = volumes + end + + def to_s + "Podman Volumes" + end + + def resource_id + "Podman Volumes" + end + end +end diff --git a/lib/inspec/resources/podman_container.rb b/lib/inspec/resources/podman_container.rb new file mode 100644 index 000000000..86aef3c7f --- /dev/null +++ b/lib/inspec/resources/podman_container.rb @@ -0,0 +1,84 @@ +require "inspec/resources/podman" +require_relative "docker_object" + +# Change module if required +module Inspec::Resources + class PodmanContainer < Inspec.resource(1) + include Inspec::Resources::DockerObject + name "podman_container" + supports platform: "unix" + + desc "Inspec core resource to retrieve information about podman container" + + example <<~EXAMPLE + describe podman_container("sweet_mendeleev") do + it { should exist } + it { should be_running } + its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" } + its("image") { should eq "docker.io/library/nginx:latest" } + its("labels") { should include "maintainer"=>"NGINX Docker Maintainers " } + its("ports") { should eq nil } + end + + describe podman_container(id: "591270d8d80d2667") do + it { should exist } + it { should be_running } + end + EXAMPLE + + def initialize(opts = {}) + skip_resource "The `podman_container` resource is not yet available on your OS." unless inspec.os.unix? + + # if a string is provided, we expect it is the name + if opts.is_a?(String) + @opts = { name: opts } + else + @opts = opts + end + end + + def running? + status.downcase.start_with?("up") if object_info.entries.length == 1 + end + + def status + object_info.status[0] if object_info.entries.length == 1 + end + + def labels + object_info.labels + end + + def ports + object_info.ports[0] if object_info.entries.length == 1 + end + + def command + return unless object_info.entries.length == 1 + + object_info.commands[0] + end + + def image + object_info.images[0] if object_info.entries.length == 1 + end + + def resource_id + object_info.ids[0] || @opts[:id] || @opts[:name] || "" + end + + def to_s + name = @opts[:name] || @opts[:id] + "Podman Container #{name}" + end + + private + + def object_info + return @info if defined?(@info) + + opts = @opts + @info = inspec.podman.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) } + end + end +end diff --git a/lib/inspec/resources/podman_image.rb b/lib/inspec/resources/podman_image.rb new file mode 100644 index 000000000..5fa72f9c6 --- /dev/null +++ b/lib/inspec/resources/podman_image.rb @@ -0,0 +1,108 @@ +require "inspec/resources/command" +require_relative "docker_object" +require "inspec/utils/podman" + +module Inspec::Resources + class PodmanImage < Inspec.resource(1) + include Inspec::Resources::DockerObject + include Inspec::Utils::Podman + + name "podman_image" + supports platform: "unix" + + desc "InSpec core resource to retrieve information about podman image" + + example <<~EXAMPLE + describe podman_image("docker.io/library/busybox") do + it { should exist } + its("repo_tags") { should include "docker.io/library/busybox:latest" } + its("size") { should eq 1636053 } + its("resource_id") { should eq "docker.io/library/busybox:latest" } + end + + describe podman_image("docker.io/library/busybox:latest") do + it { should exist } + end + + describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do + it { should exist } + end + + describe podman_image(id: "3c19bafed223") do + it { should exist } + end + EXAMPLE + + attr_reader :opts, :image_info + + def initialize(opts) + skip_resource "The `podman_image` resource is not yet available on your OS." unless inspec.os.unix? + opts = { image: opts } if opts.is_a?(String) + @opts = sanitize_options(opts) + raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running? + + @image_info = get_image_info + end + + LABELS = { + "id" => "ID", + "repo_tags" => "RepoTags", + "size" => "Size", + "digest" => "Digest", + "created_at" => "Created", + "version" => "Version", + "names_history" => "NamesHistory", + "repo_digests" => "RepoDigests", + "architecture" => "Architecture", + "os" => "Os", + "virtual_size" => "VirtualSize", + }.freeze + + ## This creates all the required properties methods dynamically. + LABELS.each do |k, v| + define_method(k) do + image_info[k.to_s] + end + end + + def exist? + ! image_info.empty? + end + + def resource_id + opts[:id] || opts[:image] || "" + end + + def to_s + "podman_image #{resource_id}" + end + + private + + def sanitize_options(opts) + opts.merge!(parse_components_from_image(opts[:image])) + + # assume a "latest" tag if we don't have one + opts[:tag] ||= "latest" + + # Assemble/reassemble the image from the repo and tag + opts[:image] = "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil? + + opts + end + + def get_image_info + current_image = opts[:id] || opts[:image] || opts[:repo] + ":" + opts[:tag] + json_key_label = generate_go_template(LABELS) + podman_inspect_cmd = inspec.command("podman image inspect #{current_image} --format '{#{json_key_label}}'") + + if podman_inspect_cmd.exit_status == 0 + parse_command_output(podman_inspect_cmd.stdout) + elsif podman_inspect_cmd.stderr =~ /failed to find image/ + {} + else + raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman image information for #{current_image}.\nError message: #{podman_inspect_cmd.stderr}" + end + end + end +end diff --git a/lib/inspec/resources/podman_network.rb b/lib/inspec/resources/podman_network.rb new file mode 100644 index 000000000..e9b604da9 --- /dev/null +++ b/lib/inspec/resources/podman_network.rb @@ -0,0 +1,81 @@ +require "inspec/resources/command" +require "inspec/utils/podman" +module Inspec::Resources + class PodmanNetwork < Inspec.resource(1) + include Inspec::Utils::Podman + + name "podman_network" + + supports platform: "unix" + + desc "InSpec core resource to retrive information about the given Podman network" + + example <<~EXAMPLE + describe podman_network("podman") do + it { should exist } + end + describe podman_network("3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f") do + its("driver") { should eq bridge } + end + EXAMPLE + + LABELS = { + id: "ID", + name: "Name", + driver: "Driver", + labels: "Labels", + options: "Options", + ipam_options: "IPAMOptions", + internal: "Internal", + created: "Created", + ipv6_enabled: "IPv6Enabled", + dns_enabled: "DNSEnabled", + network_interface: "NetworkInterface", + subnets: "Subnets", + }.freeze + + attr_reader :param, :network_info + def initialize(param) + skip_resource "The `podman_network` resource is not yet available on your OS." unless inspec.os.unix? + + @param = param + raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running? + + @network_info = get_network_info + end + + ## This creates all the required properties methods dynamically. + LABELS.each do |k, v| + define_method(k) do + network_info[k.to_s] + end + end + + def exist? + !network_info.empty? + end + + def resource_id + id || param || "" + end + + def to_s + "podman_network #{resource_id}" + end + + private + + def get_network_info + go_template_format = generate_go_template(LABELS) + result = inspec.command("podman network inspect #{param} --format '{#{go_template_format}}'") + + if result.exit_status == 0 + parse_command_output(result.stdout) + elsif result.stderr =~ /network not found/ + {} + else + raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman network information for #{param}.\nError message: #{result.stderr}" + end + end + end +end diff --git a/lib/inspec/resources/podman_pod.rb b/lib/inspec/resources/podman_pod.rb new file mode 100644 index 000000000..cc3d0deca --- /dev/null +++ b/lib/inspec/resources/podman_pod.rb @@ -0,0 +1,101 @@ +require "inspec/resources/command" +require "inspec/utils/podman" + +module Inspec::Resources + class PodmanPod < Inspec.resource(1) + include Inspec::Utils::Podman + + name "podman_pod" + supports platform: "unix" + + desc "InSpec core resource to retrieve information about podman pod" + + example <<~EXAMPLE + describe podman_pod("nginx-frontend") do + it { should exist } + its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" } + its("name") { should eq "nginx-frontend" } + its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" } + its("create_command") { should include "new:nginx-frontend" } + its("state") { should eq "Running" } + its("hostname") { should eq "" } + its("create_cgroup") { should eq true } + its("cgroup_parent") { should eq "user.slice" } + its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" } + its("create_infra") { should eq true } + its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" } + its("infra_config") { should include "DNSOption" } + its("shared_namespaces") { should include "ipc" } + its("num_containers") { should eq 2 } + its("containers") { should_not be nil } + end + + describe podman_pod("non-existing-pod") do + it { should_not exist } + end + EXAMPLE + + attr_reader :pod_info, :pod_id + + def initialize(pod_id) + skip_resource "The `podman_pod` resource is not yet available on your OS." unless inspec.os.unix? + raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running? + + @pod_id = pod_id + @pod_info = get_pod_info + end + + LABELS = { + "id" => "ID", + "name" => "Name", + "created_at" => "Created", + "create_command" => "CreateCommand", + "state" => "State", + "hostname" => "Hostname", + "create_cgroup" => "CreateCgroup", + "cgroup_parent" => "CgroupParent", + "cgroup_path" => "CgroupPath", + "create_infra" => "CreateInfra", + "infra_container_id" => "InfraContainerID", + "infra_config" => "InfraConfig", + "shared_namespaces" => "SharedNamespaces", + "num_containers" => "NumContainers", + "containers" => "Containers", + }.freeze + + # This creates all the required properties methods dynamically. + LABELS.each do |k, _| + define_method(k) do + pod_info[k.to_s] + end + end + + def exist? + !pod_info.empty? + end + + def resource_id + pod_id + end + + def to_s + "Podman Pod #{resource_id}" + end + + private + + def get_pod_info + json_key_label = generate_go_template(LABELS) + + inspect_pod_cmd = inspec.command("podman pod inspect #{pod_id} --format '{#{json_key_label}}'") + + if inspect_pod_cmd.exit_status == 0 + parse_command_output(inspect_pod_cmd.stdout) + elsif inspect_pod_cmd.stderr =~ /no pod with name or ID/ + {} + else + raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman pod information for #{pod_id}.\nError message: #{inspect_pod_cmd.stderr}" + end + end + end +end diff --git a/lib/inspec/resources/podman_volume.rb b/lib/inspec/resources/podman_volume.rb new file mode 100644 index 000000000..099512dfa --- /dev/null +++ b/lib/inspec/resources/podman_volume.rb @@ -0,0 +1,87 @@ +require "inspec/resources/command" +require "inspec/utils/podman" + +module Inspec::Resources + class PodmanVolume < Inspec.resource(1) + include Inspec::Utils::Podman + + name "podman_volume" + supports platform: "unix" + + desc "InSpec core resource to retrieve information about podman volume" + + example <<~EXAMPLE + describe podman_volume("my_volume") do + it { should exist } + its("name") { should eq "my_volume" } + its("driver") { should eq "local" } + its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" } + its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" } + its("labels") { should eq({}) } + its("scope") { should eq "local" } + its("options") { should eq({}) } + its("mount_count") { should eq 0 } + its("needs_copy_up") { should eq true } + its("needs_chown") { should eq true } + end + EXAMPLE + + attr_reader :volume_info, :volume_name + + def initialize(volume_name) + skip_resource "The `podman_volume` resource is not yet available on your OS." unless inspec.os.unix? + raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running? + + @volume_name = volume_name + @volume_info = get_volume_info + end + + LABELS = { + "name" => "Name", + "driver" => "Driver", + "mountpoint" => "Mountpoint", + "created_at" => "CreatedAt", + "labels" => "Labels", + "scope" => "Scope", + "options" => "Options", + "mount_count" => "MountCount", + "needs_copy_up" => "NeedsCopyUp", + "needs_chown" => "NeedsChown", + }.freeze + + # This creates all the required properties methods dynamically. + LABELS.each do |k, _| + define_method(k) do + volume_info[k.to_s] + end + end + + def exist? + !volume_info.empty? + end + + def resource_id + volume_name + end + + def to_s + "podman_volume #{resource_id}" + end + + private + + def get_volume_info + json_key_label = generate_go_template(LABELS) + + inspect_volume_cmd = inspec.command("podman volume inspect #{volume_name} --format '{#{json_key_label}}'") + + if inspect_volume_cmd.exit_status == 0 + parse_command_output(inspect_volume_cmd.stdout) + elsif inspect_volume_cmd.stderr =~ /inspecting object: no such/ + {} + else + raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman volume information for #{volume_name}.\nError message: #{inspect_volume_cmd.stderr}" + end + end + end +end diff --git a/lib/inspec/utils/podman.rb b/lib/inspec/utils/podman.rb new file mode 100644 index 000000000..69247e15a --- /dev/null +++ b/lib/inspec/utils/podman.rb @@ -0,0 +1,24 @@ +require "inspec/resources/command" + +module Inspec + module Utils + module Podman + def podman_running? + inspec.command("podman version").exit_status == 0 + end + + # Generates the template in this format using labels hash: "\"id\": {{json .ID}}, \"name\": {{json .Name}}", + def generate_go_template(labels) + (labels.map { |k, v| "\"#{k}\": {{json .#{v}}}" }).join(", ") + end + + def parse_command_output(output) + require "json" unless defined?(JSON) + JSON.parse(output) + rescue JSON::ParserError => _e + warn "Could not parse the command output" + {} + end + end + end +end \ No newline at end of file diff --git a/test/fixtures/cmd/podman-errors b/test/fixtures/cmd/podman-errors new file mode 100644 index 000000000..de08dcc8c --- /dev/null +++ b/test/fixtures/cmd/podman-errors @@ -0,0 +1,6 @@ +Error: inspecting object: network min: network not found +Error: inspecting object: unable to inspect \"abc\": failed to find image abc: abc: image not known +Error: no pod with name or ID non_existing_pod found: no such pod +[] +error inspecting object: no such object: "non_existing_volume" +Error: inspecting object: no such object: "volume" \ No newline at end of file diff --git a/test/fixtures/cmd/podman-images-a b/test/fixtures/cmd/podman-images-a new file mode 100644 index 000000000..f3c927482 --- /dev/null +++ b/test/fixtures/cmd/podman-images-a @@ -0,0 +1,4 @@ +{ "id": "sha256:c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f", "repository": "localhost/podman-pause", "tag": "4.1.0-1651853754", "size": "816 kB", "digest": "sha256:e6e9fffed42f600c811af34569268c07d063f12507457493c608d944a1fdac3f", "createdat": "2022-07-01 07:38:09 +0000 UTC", "createdsince": "5 days ago", "history": "localhost/podman-pause:4.1.0-1651853754" } +{ "id": "sha256:55f4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb", "repository": "docker.io/library/nginx", "tag": "latest", "size": "146 MB", "digest": "sha256:10f14ffa93f8dedf1057897b745e5ac72ac5655c299dade0aa434c71557697ea", "createdat": "2022-06-23 04:13:24 +0000 UTC", "createdsince": "13 days ago", "history": "docker.io/library/nginx:latest" } +{ "id": "sha256:27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee", "repository": "docker.io/library/ubuntu", "tag": "latest", "size": "80.3 MB", "digest": "sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac", "createdat": "2022-06-06 22:21:26 +0000 UTC", "createdsince": "4 weeks ago", "history": "docker.io/library/ubuntu:latest" } +{ "id": "sha256:3a66698e604003f7822a0c73e9da50e090fda9a99fe1f2e1e2e7fe796cc803d5", "repository": "registry.fedoraproject.org/fedora", "tag": "latest", "size": "169 MB", "digest": "sha256:38813cf0913241b7f13c7057e122f7c3cfa2e7c427dca3194f933d94612e280b", "createdat": "2022-05-06 10:11:58 +0000 UTC", "createdsince": "2 months ago", "history": "registry.fedoraproject.org/fedora:latest" } \ No newline at end of file diff --git a/test/fixtures/cmd/podman-info b/test/fixtures/cmd/podman-info new file mode 100644 index 000000000..3eaa0a66d --- /dev/null +++ b/test/fixtures/cmd/podman-info @@ -0,0 +1,150 @@ +{ + "host": { + "arch": "amd64", + "buildahVersion": "1.26.1", + "cgroupManager": "systemd", + "cgroupVersion": "v2", + "cgroupControllers": [ + "cpu", + "io", + "memory", + "pids" + ], + "conmon": { + "package": "conmon-2.1.0-2.fc36.x86_64", + "path": "/usr/bin/conmon", + "version": "conmon version 2.1.0, commit: " + }, + "cpus": 1, + "cpuUtilization": { + "userPercent": 0.03, + "systemPercent": 0.09, + "idlePercent": 99.89 + }, + "distribution": { + "distribution": "fedora", + "variant": "coreos", + "version": "36" + }, + "eventLogger": "journald", + "hostname": "localhost.localdomain", + "idMappings": { + "gidmap": [ + { + "container_id": 0, + "host_id": 1000, + "size": 1 + }, + { + "container_id": 1, + "host_id": 100000, + "size": 1000000 + } + ], + "uidmap": [ + { + "container_id": 0, + "host_id": 1005691005, + "size": 1 + }, + { + "container_id": 1, + "host_id": 100000, + "size": 1000000 + } + ] + }, + "kernel": "5.17.5-300.fc36.x86_64", + "logDriver": "journald", + "memFree": 1668063232, + "memTotal": 2066817024, + "networkBackend": "netavark", + "ociRuntime": { + "name": "crun", + "package": "crun-1.4.4-1.fc36.x86_64", + "path": "/usr/bin/crun", + "version": "crun version 1.4.4\ncommit: 6521fcc5806f20f6187eb933f9f45130c86da230\nspec: 1.0.0\n+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL" + }, + "os": "linux", + "remoteSocket": { + "path": "/run/user/1005691005/podman/podman.sock", + "exists": true + }, + "serviceIsRemote": true, + "security": { + "apparmorEnabled": false, + "capabilities": "CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT", + "rootless": true, + "seccompEnabled": true, + "seccompProfilePath": "/usr/share/containers/seccomp.json", + "selinuxEnabled": true + }, + "slirp4netns": { + "executable": "/usr/bin/slirp4netns", + "package": "slirp4netns-1.2.0-0.2.beta.0.fc36.x86_64", + "version": "slirp4netns version 1.2.0-beta.0\ncommit: 477db14a24ff1a3de3a705e51ca2c4c1fe3dda64\nlibslirp: 4.6.1\nSLIRP_CONFIG_VERSION_MAX: 3\nlibseccomp: 2.5.3" + }, + "swapFree": 0, + "swapTotal": 0, + "uptime": "12h 40m 12.19s (Approximately 0.50 days)", + "linkmode": "dynamic" + }, + "store": { + "configFile": "/var/home/core/.config/containers/storage.conf", + "containerStore": { + "number": 5, + "paused": 0, + "running": 3, + "stopped": 2 + }, + "graphDriverName": "overlay", + "graphOptions": { + + }, + "graphRoot": "/var/home/core/.local/share/containers/storage", + "graphRootAllocated": 106825756672, + "graphRootUsed": 2833563648, + "graphStatus": { + "Backing Filesystem": "xfs", + "Native Overlay Diff": "true", + "Supports d_type": "true", + "Using metacopy": "false" + }, + "imageCopyTmpDir": "/var/tmp", + "imageStore": { + "number": 4 + }, + "runRoot": "/run/user/1005691005/containers", + "volumePath": "/var/home/core/.local/share/containers/storage/volumes" + }, + "registries": { + "search": [ + "docker.io" +] + }, + "plugins": { + "volume": [ + "local" + ], + "network": [ + "bridge", + "macvlan" + ], + "log": [ + "k8s-file", + "none", + "passthrough", + "journald" + ] + }, + "version": { + "APIVersion": "4.1.0", + "Version": "4.1.0", + "GoVersion": "go1.18", + "GitCommit": "", + "BuiltTime": "Fri May 6 21:45:54 2022", + "Built": 1651853754, + "OsArch": "linux/amd64", + "Os": "linux" + } +} diff --git a/test/fixtures/cmd/podman-inspec b/test/fixtures/cmd/podman-inspec new file mode 100644 index 000000000..b8df0e831 --- /dev/null +++ b/test/fixtures/cmd/podman-inspec @@ -0,0 +1,10 @@ +[ + { + "Id": "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7", + "Created": "2022-06-28T16:34:10.965113607+05:30", + "Path": "/docker-entrypoint.sh", + "Args": [ + "/bin/bash" + ] + } +] diff --git a/test/fixtures/cmd/podman-inspect-info b/test/fixtures/cmd/podman-inspect-info new file mode 100644 index 000000000..4e4682bbb --- /dev/null +++ b/test/fixtures/cmd/podman-inspect-info @@ -0,0 +1 @@ +{"id": "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6", "repo_tags": ["docker.io/library/busybox:latest"], "size": 1636053, "digest": "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83", "created_at": "2022-06-08T00:39:28.175020858Z", "version": "20.10.12", "names_history": ["docker.io/library/busybox:latest"], "repo_digests": ["docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a","docker.io/library/busybox@sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83"], "architecture": "arm64", "os": "linux", "virtual_size": 1636053} \ No newline at end of file diff --git a/test/fixtures/cmd/podman-network b/test/fixtures/cmd/podman-network new file mode 100644 index 000000000..663a9ccc1 --- /dev/null +++ b/test/fixtures/cmd/podman-network @@ -0,0 +1 @@ +{ "id": "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f", "name": "minikube", "driver": "bridge", "labels": {"created_by.minikube.sigs.k8s.io": "true", "name.minikube.sigs.k8s.io": "minikube"}, "options": null, "ipam_options": {"driver": "host-local"}, "internal": false, "created": "2022-07-10T19:37:11.656610731+05:30", "ipv6_enabled": false, "dns_enabled": true, "network_interface": "podman1", "subnets": [{"subnet": "192.168.49.0/24", "gateway": "192.168.49.1"}] } \ No newline at end of file diff --git a/test/fixtures/cmd/podman-network-ls b/test/fixtures/cmd/podman-network-ls new file mode 100644 index 000000000..e912dc59d --- /dev/null +++ b/test/fixtures/cmd/podman-network-ls @@ -0,0 +1 @@ +{ "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", "name": "podman", "driver": "bridge", "labels": "", "options": null, "IpamOptions": {"driver":"host-local"}, "created": "2022-07-06T10:32:00.879655095+05:30", "internal": false, "Ipv6Enabled": false, "DnsEnabled": false, "NetworkInterface": "podman0", "Subnets": [{"subnet":"10.88.0.0/16","gateway":"10.88.0.1"}] } \ No newline at end of file diff --git a/test/fixtures/cmd/podman-pod-inspect b/test/fixtures/cmd/podman-pod-inspect new file mode 100644 index 000000000..079602f55 --- /dev/null +++ b/test/fixtures/cmd/podman-pod-inspect @@ -0,0 +1 @@ +{"id": "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4", "name": "nginx-frontend", "created_at": "2022-07-14T15:47:47.978078124+05:30", "create_command": ["podman","run","-dt","--pod","new:nginx-frontend","-p","8080:80","nginx"], "state": "Running", "hostname": "", "create_cgroup": true, "cgroup_parent": "user.slice", "cgroup_path": "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice", "create_infra": true, "infra_container_id": "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251", "infra_config": {"PortBindings":{"80/tcp":[{"HostIp":"","HostPort":"8080"}]},"HostNetwork":false,"StaticIP":"","StaticMAC":"","NoManageResolvConf":false,"DNSServer":null,"DNSSearch":null,"DNSOption":null,"NoManageHosts":false,"HostAdd":null,"Networks":["podman"],"NetworkOptions":null,"pid_ns":"private","userns":"host"}, "shared_namespaces": ["uts","ipc","net"], "num_containers": 2, "containers": [{"Id":"3c8a4782f3401033a2ff2bedd9c002762c9c47e6194ceafbb6cfed8312b24de9","Name":"epic_hodgkin","State":"running"},{"Id":"727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251","Name":"fcfe4d471cff-infra","State":"running"}]} \ No newline at end of file diff --git a/test/fixtures/cmd/podman-pod-ps b/test/fixtures/cmd/podman-pod-ps new file mode 100644 index 000000000..9c34610d7 --- /dev/null +++ b/test/fixtures/cmd/podman-pod-ps @@ -0,0 +1,29 @@ +[ + { + "Cgroup": "user.slice", + "Containers": [ + { + "Id": "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", + "Names": "95cadbb84df7-infra", + "Status": "running" + }, + { + "Id": "b36abf69b8af6f8a8305ab2d9b209c2acaeece41dbc4f242f8e45caf6e02504b", + "Names": "pensive_mccarthy", + "Status": "running" + } + ], + "Created": "2022-07-01T13:08:09.662082101+05:30", + "Id": "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc", + "InfraId": "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", + "Name": "cranky_allen", + "Namespace": "", + "Networks": [ + "podman" + ], + "Status": "Running", + "Labels": { + + } + } +] diff --git a/test/fixtures/cmd/podman-ps-a b/test/fixtures/cmd/podman-ps-a new file mode 100644 index 000000000..f964292d0 --- /dev/null +++ b/test/fixtures/cmd/podman-ps-a @@ -0,0 +1,5 @@ +{ "id": "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7", "image": "docker.io/library/nginx:latest", "image_id": "55f4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb", "command": "/bin/bash", "createdat": "2022-06-28 16:34:10.965113607 +0530 IST", "running_for": "8 days ago", "status": "Up 13 hours ago", "pod": "", "ports": "", "size": "12B (virtual 142MB)", "names": "sweet_mendeleev", "networks": "podman", "labels": {"maintainer":"NGINX Docker Maintainers "}, "mounts": [] } +{ "id": "64b5562346d6b52fd40d790b34e9f18ba3b8745649c302b79ba5399d4ea00b36", "image": "docker.io/library/ubuntu:latest", "image_id": "27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee", "command": "/bin/bash", "createdat": "2022-06-29 08:48:45.195339311 +0530 IST", "running_for": "7 days ago", "status": "Up 13 hours ago", "pod": "", "ports": "", "size": "12B (virtual 77.8MB)", "names": "wizardly_torvalds", "networks": "podman", "labels": null, "mounts": [] } +{ "id": "437e70c45633de74be7a87ed8d94c442a3bfe0a1cdd293d5184a4af1765d8cf5", "image": "registry.fedoraproject.org/fedora:latest", "image_id": "3a66698e604003f7822a0c73e9da50e090fda9a99fe1f2e1e2e7fe796cc803d5", "command": "/bin/bash", "createdat": "2022-06-29 13:40:20.414848724 +0530 IST", "running_for": "7 days ago", "status": "Created", "pod": "", "ports": "", "size": "0B (virtual 163MB)", "names": "confident_bell", "networks": "podman", "labels": {"license":"MIT","name":"fedora","vendor":"Fedora Project","version":"36"}, "mounts": [] } +{ "id": "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", "image": "localhost/podman-pause:4.1.0-1651853754", "image_id": "c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f", "command": "", "createdat": "2022-07-01 13:08:09.685404054 +0530 IST", "running_for": "5 days ago", "status": "Created", "pod": "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc", "ports": "", "size": "12B (virtual 812kB)", "names": "95cadbb84df7-infra", "networks": "podman", "labels": {"io.buildah.version":"1.26.1"}, "mounts": [] } +{ "id": "b36abf69b8af6f8a8305ab2d9b209c2acaeece41dbc4f242f8e45caf6e02504b", "image": "docker.io/library/ubuntu:latest", "image_id": "27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee", "command": "bash", "createdat": "2022-07-01 22:05:09.624021187 +0530 IST", "running_for": "4 days ago", "status": "Created", "pod": "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc", "ports": "", "size": "12B (virtual 77.8MB)", "names": "pensive_mccarthy", "networks": "", "labels": null, "mounts": [] } \ No newline at end of file diff --git a/test/fixtures/cmd/podman-version b/test/fixtures/cmd/podman-version new file mode 100644 index 000000000..7f1c51bfb --- /dev/null +++ b/test/fixtures/cmd/podman-version @@ -0,0 +1 @@ +{"Client":{"APIVersion":"4.1.0","Version":"4.1.0","GoVersion":"go1.18.1","GitCommit":"","BuiltTime":"Fri May 6 01:37:47 2022","Built":1651781267,"OsArch":"darwin/amd64","Os":"darwin"},"Server":{"APIVersion":"4.1.0","Version":"4.1.0","GoVersion":"go1.18","GitCommit":"","BuiltTime":"Fri May 6 21:45:54 2022","Built":1651853754,"OsArch":"linux/amd64","Os":"linux"}} diff --git a/test/fixtures/cmd/podman-volume-inspect b/test/fixtures/cmd/podman-volume-inspect new file mode 100644 index 000000000..11b3fc0b1 --- /dev/null +++ b/test/fixtures/cmd/podman-volume-inspect @@ -0,0 +1 @@ +{"name": "my_volume", "driver": "local", "mountpoint": "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data", "created_at": "2022-07-14T13:21:19.965421792+05:30", "labels": {}, "scope": "local", "options": {}, "mount_count": 0, "needs_copy_up": true, "needs_chown": true} \ No newline at end of file diff --git a/test/fixtures/cmd/podman-volume-ls b/test/fixtures/cmd/podman-volume-ls new file mode 100644 index 000000000..a28945b1f --- /dev/null +++ b/test/fixtures/cmd/podman-volume-ls @@ -0,0 +1,18 @@ +[ + { + "Name": "ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a", + "Driver": "local", + "Mountpoint": "/var/home/core/.local/share/containers/storage/volumes/ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a/_data", + "CreatedAt": "2022-07-02T12:40:37.012062614+05:30", + "Labels": { + + }, + "Scope": "local", + "Options": { + + }, + "MountCount": 0, + "NeedsCopyUp": true, + "NeedsChown": true + } +] diff --git a/test/helpers/mock_loader.rb b/test/helpers/mock_loader.rb index c1c7c0386..11e45d795 100644 --- a/test/helpers/mock_loader.rb +++ b/test/helpers/mock_loader.rb @@ -681,6 +681,25 @@ class MockLoader # file resource windows inherit "(Get-Acl 'C:/ExamlpeFolder').access| Where-Object {$_.IsInherited -eq $true} | measure | % { $_.Count }" => cmd.call("windows_file_inherit_output"), + + # podman + %{podman ps -a --no-trunc --size --format '{\"ID\": {{json .ID}}, \"Image\": {{json .Image}}, \"ImageID\": {{json .ImageID}}, \"Command\": {{json .Command}}, \"CreatedAt\": {{json .CreatedAt}}, \"RunningFor\": {{json .RunningFor}}, \"Status\": {{json .Status}}, \"Pod\": {{json .Pod}}, \"Ports\": {{json .Ports}}, \"Size\": {{json .Size}}, \"Names\": {{json .Names}}, \"Networks\": {{json .Networks}}, \"Labels\": {{json .Labels}}, \"Mounts\": {{json .Mounts}}}'} => cmd.call("podman-ps-a"), + %{podman images -a --no-trunc --format '{\"ID\": {{json .ID}}, \"Repository\": {{json .Repository}}, \"Tag\": {{json .Tag}}, \"Size\": {{json .Size}}, \"Digest\": {{json .Digest}}, \"CreatedAt\": {{json .CreatedAt}}, \"CreatedSince\": {{json .CreatedSince}}, \"History\": {{json .History}}}'} => cmd.call("podman-images-a"), + %{podman network ls --no-trunc --format '{\"ID\": {{json .ID}}, \"Name\": {{json .Name}}, \"Driver\": {{json .Driver}}, \"Labels\": {{json .Labels}}, \"Options\": {{json .Options}}, \"IPAMOptions\": {{json .IPAMOptions}}, \"Created\": {{json .Created}}, \"Internal\": {{json .Internal}}, \"IPv6Enabled\": {{json .IPv6Enabled}}, \"DNSEnabled\": {{json .DNSEnabled}}, \"NetworkInterface\": {{json .NetworkInterface}}, \"Subnets\": {{json .Subnets}}}'} => cmd.call("podman-network-ls"), + "podman pod ps --no-trunc --format json" => cmd.call("podman-pod-ps"), + "podman info --format json" => cmd.call("podman-info"), + "podman version --format json" => cmd.call("podman-version"), + "podman volume ls --format json" => cmd.call("podman-volume-ls"), + "podman inspect 591270d8d80d --format json" => cmd.call("podman-inspec"), + "podman image inspect docker.io/library/busybox:latest --format '{\"id\": {{json .ID}}, \"repo_tags\": {{json .RepoTags}}, \"size\": {{json .Size}}, \"digest\": {{json .Digest}}, \"created_at\": {{json .Created}}, \"version\": {{json .Version}}, \"names_history\": {{json .NamesHistory}}, \"repo_digests\": {{json .RepoDigests}}, \"architecture\": {{json .Architecture}}, \"os\": {{json .Os}}, \"virtual_size\": {{json .VirtualSize}}}'" => cmd.call("podman-inspect-info"), + "podman image inspect not-exist:latest --format '{\"id\": {{json .ID}}, \"repo_tags\": {{json .RepoTags}}, \"size\": {{json .Size}}, \"digest\": {{json .Digest}}, \"created_at\": {{json .Created}}, \"version\": {{json .Version}}, \"names_history\": {{json .NamesHistory}}, \"repo_digests\": {{json .RepoDigests}}, \"architecture\": {{json .Architecture}}, \"os\": {{json .Os}}, \"virtual_size\": {{json .VirtualSize}}}'" => cmd_stderr.call("podman-errors"), + "podman network inspect minikube --format '{\"id\": {{json .ID}}, \"name\": {{json .Name}}, \"driver\": {{json .Driver}}, \"labels\": {{json .Labels}}, \"options\": {{json .Options}}, \"ipam_options\": {{json .IPAMOptions}}, \"internal\": {{json .Internal}}, \"created\": {{json .Created}}, \"ipv6_enabled\": {{json .IPv6Enabled}}, \"dns_enabled\": {{json .DNSEnabled}}, \"network_interface\": {{json .NetworkInterface}}, \"subnets\": {{json .Subnets}}}'" => cmd.call("podman-network"), + "podman network inspect not-exist --format '{\"id\": {{json .ID}}, \"name\": {{json .Name}}, \"driver\": {{json .Driver}}, \"labels\": {{json .Labels}}, \"options\": {{json .Options}}, \"ipam_options\": {{json .IPAMOptions}}, \"internal\": {{json .Internal}}, \"created\": {{json .Created}}, \"ipv6_enabled\": {{json .IPv6Enabled}}, \"dns_enabled\": {{json .DNSEnabled}}, \"network_interface\": {{json .NetworkInterface}}, \"subnets\": {{json .Subnets}}}'" => cmd_stderr.call("podman-errors"), + "podman version" => empty.call, + "podman volume inspect my_volume --format '{\"name\": {{json .Name}}, \"driver\": {{json .Driver}}, \"mountpoint\": {{json .Mountpoint}}, \"created_at\": {{json .CreatedAt}}, \"labels\": {{json .Labels}}, \"scope\": {{json .Scope}}, \"options\": {{json .Options}}, \"mount_count\": {{json .MountCount}}, \"needs_copy_up\": {{json .NeedsCopyUp}}, \"needs_chown\": {{json .NeedsChown}}}'" => cmd.call("podman-volume-inspect"), + "podman volume inspect non_existing_volume --format '{\"name\": {{json .Name}}, \"driver\": {{json .Driver}}, \"mountpoint\": {{json .Mountpoint}}, \"created_at\": {{json .CreatedAt}}, \"labels\": {{json .Labels}}, \"scope\": {{json .Scope}}, \"options\": {{json .Options}}, \"mount_count\": {{json .MountCount}}, \"needs_copy_up\": {{json .NeedsCopyUp}}, \"needs_chown\": {{json .NeedsChown}}}'" => cmd_stderr.call("podman-errors"), + "podman pod inspect nginx-frontend --format '{\"id\": {{json .ID}}, \"name\": {{json .Name}}, \"created_at\": {{json .Created}}, \"create_command\": {{json .CreateCommand}}, \"state\": {{json .State}}, \"hostname\": {{json .Hostname}}, \"create_cgroup\": {{json .CreateCgroup}}, \"cgroup_parent\": {{json .CgroupParent}}, \"cgroup_path\": {{json .CgroupPath}}, \"create_infra\": {{json .CreateInfra}}, \"infra_container_id\": {{json .InfraContainerID}}, \"infra_config\": {{json .InfraConfig}}, \"shared_namespaces\": {{json .SharedNamespaces}}, \"num_containers\": {{json .NumContainers}}, \"containers\": {{json .Containers}}}'" => cmd.call("podman-pod-inspect"), + "podman pod inspect non_existing_pod --format '{\"id\": {{json .ID}}, \"name\": {{json .Name}}, \"created_at\": {{json .Created}}, \"create_command\": {{json .CreateCommand}}, \"state\": {{json .State}}, \"hostname\": {{json .Hostname}}, \"create_cgroup\": {{json .CreateCgroup}}, \"cgroup_parent\": {{json .CgroupParent}}, \"cgroup_path\": {{json .CgroupPath}}, \"create_infra\": {{json .CreateInfra}}, \"infra_container_id\": {{json .InfraContainerID}}, \"infra_config\": {{json .InfraConfig}}, \"shared_namespaces\": {{json .SharedNamespaces}}, \"num_containers\": {{json .NumContainers}}, \"containers\": {{json .Containers}}}'" => cmd_stderr.call("podman-errors"), } if @platform && (@platform[:name] == "windows" || @platform[:name] == "freebsd") diff --git a/test/unit/resources/podman_container_test.rb b/test/unit/resources/podman_container_test.rb new file mode 100644 index 000000000..7f1186518 --- /dev/null +++ b/test/unit/resources/podman_container_test.rb @@ -0,0 +1,31 @@ +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman_container" + +describe Inspec::Resources::PodmanContainer do + it "check container parsing" do + resource = load_resource("podman_container", "sweet_mendeleev") + _(resource.exist?).must_equal true + _(resource.command).must_equal "/bin/bash" + _(resource.status).must_equal "Up 13 hours ago" + _(resource.running?).must_equal true + _(resource.labels).must_include("maintainer" => "NGINX Docker Maintainers ") + _(resource.image).must_equal "docker.io/library/nginx:latest" + _(resource.ports).must_equal "" + end + + it "prints as a podman resource" do + resource = load_resource("podman_container", "sweet_mendeleev") + _(resource.to_s).must_equal "Podman Container sweet_mendeleev" + end + + it "prints the resource id of the current resource" do + resource = load_resource("podman_container", "sweet_mendeleev") + _(resource.resource_id).must_equal "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" + end + + it "skips the resource for unsupported platform" do + resource = MockLoader.new(:mock).load_resource("podman_container", "sweet_mendeleev") + _(resource.resource_skipped?).must_equal true + end +end diff --git a/test/unit/resources/podman_image_test.rb b/test/unit/resources/podman_image_test.rb new file mode 100644 index 000000000..a35758e95 --- /dev/null +++ b/test/unit/resources/podman_image_test.rb @@ -0,0 +1,37 @@ +# If we can load the InSpec globals definition file... +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman_image" + +describe Inspec::Resources::PodmanImage do + it "test podman image properties and matchers" do + resource = MockLoader.new("unix".to_sym).load_resource("podman_image", "docker.io/library/busybox") + _(resource.exist?).must_equal true + _(resource.id).must_equal "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" + _(resource.repo_tags).must_include "docker.io/library/busybox:latest" + _(resource.created_at).must_equal "2022-06-08T00:39:28.175020858Z" + _(resource.version).must_equal "20.10.12" + _(resource.size).must_equal 1636053 + _(resource.digest).must_equal "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83" + _(resource.names_history).must_include "docker.io/library/busybox:latest" + _(resource.repo_digests).must_include "docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a" + _(resource.architecture).must_equal "arm64" + _(resource.os).must_equal "linux" + _(resource.virtual_size).must_equal 1636053 + _(resource.resource_id).must_equal "docker.io/library/busybox:latest" + _(resource.to_s).must_equal "podman_image docker.io/library/busybox:latest" + end + + it "test for a non-existing container image" do + resource = MockLoader.new("ubuntu".to_sym).load_resource("podman_image", "not-exist") + _(resource.exist?).must_equal false + assert_nil resource.repo_tags + assert_nil resource.size + assert_nil resource.digest + assert_nil resource.names_history + assert_nil resource.os + assert_nil resource.virtual_size + assert_nil resource.architecture + assert_nil resource.repo_digests + end +end diff --git a/test/unit/resources/podman_network_test.rb b/test/unit/resources/podman_network_test.rb new file mode 100644 index 000000000..0400c527a --- /dev/null +++ b/test/unit/resources/podman_network_test.rb @@ -0,0 +1,125 @@ +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman_network" + +describe Inspec::Resources::PodmanNetwork do + describe "when Podman Network with given name exist" do + let(:resource) { MockLoader.new(:unix).load_resource("podman_network", "minikube") } + + describe "exist?" do + it "returns true" do + _(resource.exist?).must_equal true + end + end + + describe "id" do + it "returns the id of the network" do + _(resource.id).must_equal "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" + end + end + + describe "name" do + it "returns the name of the network" do + _(resource.name).must_equal "minikube" + end + end + + describe "network_interface" do + it "returns the network_interface of the network" do + _(resource.network_interface).must_equal "podman1" + end + end + + describe "driver" do + it "returns the driver details of the network" do + _(resource.driver).must_equal "bridge" + end + end + + describe "labels" do + it "returns the labels of the network" do + _(resource.labels).must_equal "created_by.minikube.sigs.k8s.io" => "true", "name.minikube.sigs.k8s.io" => "minikube" + end + end + + describe "options" do + it "returns the options of the network" do + assert_nil resource.options + end + end + + describe "ipv6_enabled" do + it "returns the true if the ipv6 is enabled for the network" do + _(resource.ipv6_enabled).must_equal false + end + end + + describe "ipam_options" do + it "returns the ipam options values for the Network" do + _(resource.ipam_options).must_equal "driver" => "host-local" + end + end + + describe "dns_enabled" do + it "returns true if dns is enabled for the network" do + _(resource.dns_enabled).must_equal true + end + end + + describe "subnets" do + it "returns the subnet list for the network" do + _(resource.subnets).must_equal [{ "subnet" => "192.168.49.0/24", "gateway" => "192.168.49.1" }] + end + end + + describe "internal" do + it "returns true if the network is internal" do + _(resource.internal).must_equal false + end + end + + describe "created" do + it "returns the timestamp when the network was created" do + _(resource.created).must_equal "2022-07-10T19:37:11.656610731+05:30" + end + end + + describe "to_s" do + it "returns the Podman Nework resource name string" do + _(resource.to_s).must_equal "podman_network 3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" + end + end + + describe "resource_id" do + it "returns the resource id for the current resource" do + _(resource.resource_id).must_equal "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" + end + end + end + + describe "when Podman Network with given name does not exist" do + let(:resource) { MockLoader.new(:unix).load_resource("podman_network", "not-exist") } + + describe "exist?" do + it "returns false" do + _(resource.exist?).must_equal false + end + end + + describe "all other properties" do + it "returns nil" do + assert_nil resource.name + assert_nil resource.driver + assert_nil resource.ipv6_enabled + assert_nil resource.dns_enabled + assert_nil resource.options + assert_nil resource.ipam_options + assert_nil resource.subnets + assert_nil resource.created + assert_nil resource.internal + assert_nil resource.network_interface + assert_nil resource.labels + end + end + end +end diff --git a/test/unit/resources/podman_pod_test.rb b/test/unit/resources/podman_pod_test.rb new file mode 100644 index 000000000..3fc233941 --- /dev/null +++ b/test/unit/resources/podman_pod_test.rb @@ -0,0 +1,50 @@ +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman_pod" + +describe Inspec::Resources::PodmanPod do + it "checks podman pod parameter and works correctly" do + resource = MockLoader.new("unix".to_sym).load_resource("podman_pod", "nginx-frontend") + _(resource.exist?).must_equal true + _(resource.id).must_equal "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" + _(resource.name).must_equal "nginx-frontend" + _(resource.created_at).must_equal "2022-07-14T15:47:47.978078124+05:30" + _(resource.create_command).must_include "new:nginx-frontend" + _(resource.create_command).must_include "podman" + _(resource.state).must_equal "Running" + _(resource.hostname).must_equal "" + _(resource.create_cgroup).must_equal true + _(resource.cgroup_parent).must_equal "user.slice" + _(resource.cgroup_path).must_equal "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" + _(resource.create_infra).must_equal true + _(resource.infra_container_id).must_equal "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" + _(resource.infra_config).must_include "DNSOption" + _(resource.shared_namespaces).must_include "net" + _(resource.shared_namespaces).must_include "ipc" + _(resource.num_containers).must_equal 2 + _(resource.containers).must_be_kind_of Array + _(resource.resource_id).must_equal "nginx-frontend" + _(resource.to_s).must_equal "Podman Pod nginx-frontend" + end + + it "checks for a non-existing podman pod" do + resource = MockLoader.new("unix".to_sym).load_resource("podman_pod", "non_existing_pod") + _(resource.exist?).must_equal false + assert_nil resource.name + assert_nil resource.created_at + assert_nil resource.create_command + assert_nil resource.state + assert_nil resource.hostname + assert_nil resource.create_cgroup + assert_nil resource.cgroup_parent + assert_nil resource.cgroup_path + assert_nil resource.create_infra + assert_nil resource.infra_container_id + assert_nil resource.infra_config + assert_nil resource.shared_namespaces + assert_nil resource.num_containers + assert_nil resource.containers + _(resource.resource_id).must_equal "non_existing_pod" + _(resource.to_s).must_equal "Podman Pod non_existing_pod" + end +end diff --git a/test/unit/resources/podman_test.rb b/test/unit/resources/podman_test.rb new file mode 100644 index 000000000..3a5f57fb3 --- /dev/null +++ b/test/unit/resources/podman_test.rb @@ -0,0 +1,163 @@ +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman" + +describe Inspec::Resources::Podman do + let(:resource) { load_resource("podman") } + + it "prints as a Podman resource" do + _(resource.to_s).must_equal "Podman" + end + + it "prints as Podman containers plural resource" do + _(resource.containers.to_s).must_equal "Podman Containers" + end + + it "prints the resource id of Podman containers plural resource" do + _(resource.containers.resource_id).must_equal "Podman Containers" + end + + it "returns the parsed details of Podman containers" do + _(resource.containers.exists?).must_equal true + _(resource.containers.commands).must_equal ["/bin/bash", "/bin/bash", "/bin/bash", "", "bash"] + _(resource.containers.ids).must_equal %w{591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7 64b5562346d6b52fd40d790b34e9f18ba3b8745649c302b79ba5399d4ea00b36 437e70c45633de74be7a87ed8d94c442a3bfe0a1cdd293d5184a4af1765d8cf5 a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2 b36abf69b8af6f8a8305ab2d9b209c2acaeece41dbc4f242f8e45caf6e02504b} + _(resource.containers.images).must_equal %w{docker.io/library/nginx:latest docker.io/library/ubuntu:latest registry.fedoraproject.org/fedora:latest localhost/podman-pause:4.1.0-1651853754 docker.io/library/ubuntu:latest} + _(resource.containers.names).must_equal %w{sweet_mendeleev wizardly_torvalds confident_bell 95cadbb84df7-infra pensive_mccarthy} + _(resource.containers.status).must_equal ["Up 13 hours ago", "Up 13 hours ago", "Created", "Created", "Created"] + _(resource.containers.image_ids).must_include "55f4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb" + _(resource.containers.labels).must_include "maintainer" => "NGINX Docker Maintainers " + _(resource.containers.mounts).must_include [] + _(resource.containers.pods).must_include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" + _(resource.containers.ports).must_include "" + _(resource.containers.sizes).must_include "12B (virtual 142MB)" + _(resource.containers.created_at).must_include "2022-06-29 08:48:45.195339311 +0530 IST" + _(resource.containers.networks).must_include "podman" + _(resource.containers.running_for).must_include "8 days ago" + + end + + it "returns false if container with specific id does not exist" do + _(resource.containers.where(id: "979453ff4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb").exists?).must_equal false + end + + it "prints as Podman images plural resource" do + _(resource.images.to_s).must_equal "Podman Images" + end + + it "prints the resource id of Podman images plural resource" do + _(resource.images.resource_id).must_equal "Podman Images" + end + + it "returns the parsed details of podman images" do + _(resource.images.exists?).must_equal true + _(resource.images.ids).must_equal %w{sha256:c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f sha256:55f4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb sha256:27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee sha256:3a66698e604003f7822a0c73e9da50e090fda9a99fe1f2e1e2e7fe796cc803d5} + _(resource.images.repositories).must_equal %w{localhost/podman-pause docker.io/library/nginx docker.io/library/ubuntu registry.fedoraproject.org/fedora} + _(resource.images.tags).must_equal %w{4.1.0-1651853754 latest latest latest} + _(resource.images.sizes).must_equal ["816 kB", "146 MB", "80.3 MB", "169 MB"] + _(resource.images.digests).must_equal %w{sha256:e6e9fffed42f600c811af34569268c07d063f12507457493c608d944a1fdac3f sha256:10f14ffa93f8dedf1057897b745e5ac72ac5655c299dade0aa434c71557697ea sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac sha256:38813cf0913241b7f13c7057e122f7c3cfa2e7c427dca3194f933d94612e280b} + _(resource.images.history).must_equal %w{localhost/podman-pause:4.1.0-1651853754 docker.io/library/nginx:latest docker.io/library/ubuntu:latest registry.fedoraproject.org/fedora:latest} + _(resource.images.created_since).must_equal ["5 days ago", "13 days ago", "4 weeks ago", "2 months ago"] + _(resource.images.created_at).must_equal ["2022-07-01 07:38:09 +0000 UTC", "2022-06-23 04:13:24 +0000 UTC", "2022-06-06 22:21:26 +0000 UTC", "2022-05-06 10:11:58 +0000 UTC"] + end + + it "returns false if image with specific id does not exist" do + _(resource.images.where(id: "979453ff4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb").exists?).must_equal false + end + + it "prints as Podman networks plural resource" do + _(resource.networks.to_s).must_equal "Podman Networks" + end + + it "prints the resource id of Podman networks plural resource" do + _(resource.networks.resource_id).must_equal "Podman Networks" + end + + it "returns the parsed details of podman networks" do + _(resource.networks.exists?).must_equal true + _(resource.networks.ids).must_equal %w{2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9} + _(resource.networks.names).must_equal %w{podman} + _(resource.networks.drivers).must_equal %w{bridge} + _(resource.networks.network_interfaces).must_equal %w{podman0} + _(resource.networks.created).must_equal %w{2022-07-06T10:32:00.879655095+05:30} + _(resource.networks.subnets).must_equal [[{ "subnet" => "10.88.0.0/16", "gateway" => "10.88.0.1" }]] + _(resource.networks.ipv6_enabled).must_equal [false] + _(resource.networks.internal).must_equal [false] + _(resource.networks.dns_enabled).must_equal [false] + _(resource.networks.ipam_options).must_equal [{ "driver" => "host-local" }] + _(resource.networks.labels).must_equal [""] + _(resource.networks.options).must_include nil + end + + it "returns false if network with specific id does not exist" do + _(resource.networks.where(id: "979453ff4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb").exists?).must_equal false + end + + it "returns true if network with specific id exist" do + _(resource.networks.where(id: "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9").exists?).must_equal true + end + + it "prints as Podman pods plural resource" do + _(resource.pods.to_s).must_equal "Podman Pods" + end + + it "prints the resource id of Podman pods plural resource" do + _(resource.pods.resource_id).must_equal "Podman Pods" + end + + it "returns the parsed details of podman pods" do + _(resource.pods.ids).must_equal %w{95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc} + _(resource.pods.cgroups).must_equal %w{user.slice} + _(resource.pods.containers).must_equal [[{ "Id" => "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", "Names" => "95cadbb84df7-infra", "Status" => "running" }, { "Id" => "b36abf69b8af6f8a8305ab2d9b209c2acaeece41dbc4f242f8e45caf6e02504b", "Names" => "pensive_mccarthy", "Status" => "running" }]] + _(resource.pods.created).must_equal %w{2022-07-01T13:08:09.662082101+05:30} + _(resource.pods.infraids).must_equal %w{a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2} + _(resource.pods.names).must_equal %w{cranky_allen} + _(resource.pods.namespaces).must_equal [""] + _(resource.pods.networks).must_equal [["podman"]] + _(resource.pods.status).must_equal %w{Running} + _(resource.pods.labels).must_equal [{}] + end + + it "returns false if pod with specific id does not exist" do + _(resource.pods.where(id: "979453ff4b40fe486a5b734b46bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb").exists?).must_equal false + end + + it "checks podman info parsing" do + _(resource.info.host.os).must_equal "linux" + _(resource.info.version.Version).must_equal "4.1.0" + end + + it "checks podman version parsing" do + _(resource.version.Server.Version).must_equal "4.1.0" + _(resource.version.Client.Version).must_equal "4.1.0" + end + + it "prints as Podman volumes plural resource" do + _(resource.volumes.to_s).must_equal "Podman Volumes" + end + + it "prints the resource id of Podman volumes plural resource" do + _(resource.volumes.resource_id).must_equal "Podman Volumes" + end + + it "returns parsed details of podman volumes" do + _(resource.volumes.names).must_equal %w{ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a} + _(resource.volumes.drivers).must_equal %w{local} + _(resource.volumes.mountpoints).must_equal %w{/var/home/core/.local/share/containers/storage/volumes/ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a/_data} + _(resource.volumes.createdat).must_equal %w{2022-07-02T12:40:37.012062614+05:30} + _(resource.volumes.labels).must_equal [{}] + _(resource.volumes.scopes).must_equal %w{local} + _(resource.volumes.options).must_equal [{}] + _(resource.volumes.mountcount).must_equal [0] + _(resource.volumes.needscopyup).must_equal [true] + _(resource.volumes.needschown).must_equal [true] + end + + it "returns false if volume with specific name does not exist" do + _(resource.volumes.where(name: "6bb7bf28f52fa31426bf23be068c8e7b19e58d9b8deb").exists?).must_equal false + end + + it "check podman object parsing" do + _(resource.object("591270d8d80d").Id).must_equal "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" + _(resource.object("591270d8d80d").Path).must_equal "/docker-entrypoint.sh" + end +end diff --git a/test/unit/resources/podman_volume_test.rb b/test/unit/resources/podman_volume_test.rb new file mode 100644 index 000000000..92cf5ea90 --- /dev/null +++ b/test/unit/resources/podman_volume_test.rb @@ -0,0 +1,40 @@ +require "inspec/globals" +require "#{Inspec.src_root}/test/helper" +require_relative "../../../lib/inspec/resources/podman_volume" + +describe Inspec::Resources::PodmanVolume do + it "checks podman volume parameter and works correctly" do + resource = MockLoader.new("unix".to_sym).load_resource("podman_volume", "my_volume") + _(resource.exist?).must_equal true + _(resource.name).must_equal "my_volume" + _(resource.driver).must_equal "local" + _(resource.mountpoint).must_equal "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" + _(resource.created_at).must_equal "2022-07-14T13:21:19.965421792+05:30" + _(resource.labels).must_equal({}) + _(resource.scope).must_equal "local" + _(resource.options).must_equal({}) + _(resource.mount_count).must_equal 0 + _(resource.needs_copy_up).must_equal true + _(resource.needs_chown).must_equal true + _(resource.resource_id).must_equal "my_volume" + _(resource.to_s).must_equal "podman_volume my_volume" + end + + it "checks for a non-existing podman volume" do + resource = MockLoader.new("unix".to_sym).load_resource("podman_volume", "non_existing_volume") + _(resource.exist?).must_equal false + assert_nil resource.name + assert_nil resource.driver + assert_nil resource.mountpoint + assert_nil resource.created_at + assert_nil resource.labels + assert_nil resource.scope + assert_nil resource.options + assert_nil resource.mount_count + assert_nil resource.needs_copy_up + assert_nil resource.needs_chown + _(resource.resource_id).must_equal "non_existing_volume" + _(resource.to_s).must_equal "podman_volume non_existing_volume" + end +end + From 28c41b3108ac0bad16c8502f64efe843dc4e5a8c Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 4 Aug 2022 13:15:01 +0000 Subject: [PATCH 91/93] Bump version to 5.20.0 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be3eb1562..c24c96f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.19.0](https://github.com/inspec/inspec/tree/v5.19.0) (2022-08-01) + +## [v5.20.0](https://github.com/inspec/inspec/tree/v5.20.0) (2022-08-04) #### Merged Pull Requests -- CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) +- Adds podman resources. [#6183](https://github.com/inspec/inspec/pull/6183) ([Vasu1105](https://github.com/Vasu1105)) ### Changes since 5.18.14 release #### Merged Pull Requests +- Adds podman resources. [#6183](https://github.com/inspec/inspec/pull/6183) ([Vasu1105](https://github.com/Vasu1105)) - CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) - CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) - Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) diff --git a/VERSION b/VERSION index a472b6a48..e0b67f8a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.19.0 \ No newline at end of file +5.20.0 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index b19ec4b49..c9b9788a4 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.19.0".freeze + VERSION = "5.20.0".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 973b452d4..8f2a6f34b 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.19.0".freeze + VERSION = "5.20.0".freeze end From a32a6c130f9ba7a1b6da8ccc579e4113da11da67 Mon Sep 17 00:00:00 2001 From: Vasundhara Jagdale Date: Thu, 4 Aug 2022 18:46:36 +0530 Subject: [PATCH 92/93] Fix the dependabot adding ffi (1.15.5-x64-unknown) and removing ffi (1.15.5-x64-mingw-ucrt) from the Gemfile.lock (#6213) Signed-off-by: Vasu1105 Adding x64-mingw-ucrt Signed-off-by: Vasu1105 --- omnibus/Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 39b3c4a9b..5ffd14a12 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: 7bb8c7b4fd2cc5829fba9d52df1dac683fd88069 + revision: 1d540dcdefae0fa75eb832590e85294e7c26660a branch: main specs: omnibus-software (4.0.0) @@ -34,13 +34,13 @@ GEM artifactory (3.0.15) awesome_print (1.9.2) aws-eventstream (1.2.0) - aws-partitions (1.603.0) - aws-sdk-core (3.131.2) + aws-partitions (1.608.0) + aws-sdk-core (3.131.3) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.57.0) + aws-sdk-kms (1.58.0) aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) aws-sdk-s3 (1.114.0) @@ -50,7 +50,7 @@ GEM aws-sdk-secretsmanager (1.64.0) aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sigv4 (1.5.0) + aws-sigv4 (1.5.1) aws-eventstream (~> 1, >= 1.0.2) bcrypt_pbkdf (1.1.0) bcrypt_pbkdf (1.1.0-x64-mingw32) @@ -240,7 +240,7 @@ GEM iso8601 (0.13.0) jmespath (1.6.1) json (2.6.2) - kitchen-vagrant (1.12.0) + kitchen-vagrant (1.12.1) test-kitchen (>= 1.4, < 4) libyajl2 (2.1.0) license-acceptance (2.1.13) @@ -381,7 +381,7 @@ GEM winrm-elevated (~> 1.0) winrm-fs (~> 1.1) thor (1.2.1) - toml-rb (2.1.2) + toml-rb (2.2.0) citrus (~> 3.0, > 3.0) tomlrb (1.3.0) train-core (3.10.1) @@ -465,8 +465,8 @@ GEM PLATFORMS ruby - x64-mingw x64-mingw32 + x64-unknown x86-mingw32 DEPENDENCIES From 61798ca38390ae3d432c3814a9d17174dc68db24 Mon Sep 17 00:00:00 2001 From: Chef Expeditor Date: Thu, 4 Aug 2022 13:18:01 +0000 Subject: [PATCH 93/93] Bump version to 5.20.1 by Chef Expeditor Obvious fix; these changes are the result of automation not creative thinking. --- CHANGELOG.md | 7 ++++--- VERSION | 2 +- inspec-bin/lib/inspec-bin/version.rb | 2 +- lib/inspec/version.rb | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c24c96f59..0f42f2662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,17 @@ # Change Log - -## [v5.20.0](https://github.com/inspec/inspec/tree/v5.20.0) (2022-08-04) + +## [v5.20.1](https://github.com/inspec/inspec/tree/v5.20.1) (2022-08-04) #### Merged Pull Requests -- Adds podman resources. [#6183](https://github.com/inspec/inspec/pull/6183) ([Vasu1105](https://github.com/Vasu1105)) +- Fix the dependabot adding ffi (1.15.5-x64-unknown) to omnibus bump [#6213](https://github.com/inspec/inspec/pull/6213) ([Vasu1105](https://github.com/Vasu1105)) ### Changes since 5.18.14 release #### Merged Pull Requests +- Fix the dependabot adding ffi (1.15.5-x64-unknown) to omnibus bump [#6213](https://github.com/inspec/inspec/pull/6213) ([Vasu1105](https://github.com/Vasu1105)) - Adds podman resources. [#6183](https://github.com/inspec/inspec/pull/6183) ([Vasu1105](https://github.com/Vasu1105)) - CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) - CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) diff --git a/VERSION b/VERSION index e0b67f8a2..e85ef9bb2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.20.0 \ No newline at end of file +5.20.1 \ No newline at end of file diff --git a/inspec-bin/lib/inspec-bin/version.rb b/inspec-bin/lib/inspec-bin/version.rb index c9b9788a4..972b1ea9b 100644 --- a/inspec-bin/lib/inspec-bin/version.rb +++ b/inspec-bin/lib/inspec-bin/version.rb @@ -1,5 +1,5 @@ # This file managed by automation - do not edit manually module InspecBin INSPECBIN_ROOT = File.expand_path("..", __dir__) - VERSION = "5.20.0".freeze + VERSION = "5.20.1".freeze end diff --git a/lib/inspec/version.rb b/lib/inspec/version.rb index 8f2a6f34b..1a311ff04 100644 --- a/lib/inspec/version.rb +++ b/lib/inspec/version.rb @@ -1,3 +1,3 @@ module Inspec - VERSION = "5.20.0".freeze + VERSION = "5.20.1".freeze end