inspec-core 5.18.14 → 5.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +19 -17
  3. data/inspec-core.gemspec +22 -22
  4. data/lib/inspec/base_cli.rb +19 -17
  5. data/lib/inspec/cli.rb +27 -25
  6. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  7. data/lib/inspec/dsl.rb +9 -5
  8. data/lib/inspec/enhanced_outcomes.rb +19 -0
  9. data/lib/inspec/env_printer.rb +1 -1
  10. data/lib/inspec/exceptions.rb +2 -0
  11. data/lib/inspec/formatters/base.rb +69 -16
  12. data/lib/inspec/plugin/v2/loader.rb +19 -8
  13. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
  14. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
  15. data/lib/inspec/profile.rb +9 -8
  16. data/lib/inspec/reporters/base.rb +1 -0
  17. data/lib/inspec/reporters/cli.rb +94 -3
  18. data/lib/inspec/reporters/json.rb +3 -1
  19. data/lib/inspec/reporters/yaml.rb +3 -1
  20. data/lib/inspec/reporters.rb +2 -1
  21. data/lib/inspec/resources/file.rb +1 -1
  22. data/lib/inspec/resources/http.rb +5 -5
  23. data/lib/inspec/resources/lxc.rb +65 -9
  24. data/lib/inspec/resources/mongodb_session.rb +5 -0
  25. data/lib/inspec/resources/nftables.rb +251 -0
  26. data/lib/inspec/resources/oracledb_session.rb +13 -4
  27. data/lib/inspec/resources/podman.rb +353 -0
  28. data/lib/inspec/resources/podman_container.rb +84 -0
  29. data/lib/inspec/resources/podman_image.rb +108 -0
  30. data/lib/inspec/resources/podman_network.rb +81 -0
  31. data/lib/inspec/resources/podman_pod.rb +101 -0
  32. data/lib/inspec/resources/podman_volume.rb +87 -0
  33. data/lib/inspec/resources/postgres_session.rb +2 -1
  34. data/lib/inspec/resources/service.rb +1 -1
  35. data/lib/inspec/resources.rb +1 -0
  36. data/lib/inspec/rule.rb +54 -17
  37. data/lib/inspec/run_data/control.rb +6 -0
  38. data/lib/inspec/run_data/statistics.rb +8 -2
  39. data/lib/inspec/runner.rb +18 -8
  40. data/lib/inspec/runner_rspec.rb +3 -2
  41. data/lib/inspec/schema/exec_json.rb +78 -2
  42. data/lib/inspec/schema/output_schema.rb +4 -1
  43. data/lib/inspec/schema/profile_json.rb +46 -0
  44. data/lib/inspec/schema.rb +91 -0
  45. data/lib/inspec/utils/convert.rb +8 -0
  46. data/lib/inspec/utils/podman.rb +24 -0
  47. data/lib/inspec/utils/simpleconfig.rb +10 -2
  48. data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
  49. data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
  50. data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
  51. data/lib/inspec/version.rb +1 -1
  52. data/lib/inspec/waiver_file_reader.rb +61 -0
  53. data/lib/matchers/matchers.rb +15 -2
  54. data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
  55. data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
  56. data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
  57. data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
  58. data/lib/plugins/inspec-reporter-html2/README.md +1 -1
  59. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +7 -1
  60. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +10 -6
  61. data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
  62. data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +5 -2
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
  65. metadata +26 -9
@@ -0,0 +1,84 @@
1
+ require "inspec/resources/podman"
2
+ require_relative "docker_object"
3
+
4
+ # Change module if required
5
+ module Inspec::Resources
6
+ class PodmanContainer < Inspec.resource(1)
7
+ include Inspec::Resources::DockerObject
8
+ name "podman_container"
9
+ supports platform: "unix"
10
+
11
+ desc "Inspec core resource to retrieve information about podman container"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_container("sweet_mendeleev") do
15
+ it { should exist }
16
+ it { should be_running }
17
+ its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
18
+ its("image") { should eq "docker.io/library/nginx:latest" }
19
+ its("labels") { should include "maintainer"=>"NGINX Docker Maintainers <docker-maint@nginx.com>" }
20
+ its("ports") { should eq nil }
21
+ end
22
+
23
+ describe podman_container(id: "591270d8d80d2667") do
24
+ it { should exist }
25
+ it { should be_running }
26
+ end
27
+ EXAMPLE
28
+
29
+ def initialize(opts = {})
30
+ skip_resource "The `podman_container` resource is not yet available on your OS." unless inspec.os.unix?
31
+
32
+ # if a string is provided, we expect it is the name
33
+ if opts.is_a?(String)
34
+ @opts = { name: opts }
35
+ else
36
+ @opts = opts
37
+ end
38
+ end
39
+
40
+ def running?
41
+ status.downcase.start_with?("up") if object_info.entries.length == 1
42
+ end
43
+
44
+ def status
45
+ object_info.status[0] if object_info.entries.length == 1
46
+ end
47
+
48
+ def labels
49
+ object_info.labels
50
+ end
51
+
52
+ def ports
53
+ object_info.ports[0] if object_info.entries.length == 1
54
+ end
55
+
56
+ def command
57
+ return unless object_info.entries.length == 1
58
+
59
+ object_info.commands[0]
60
+ end
61
+
62
+ def image
63
+ object_info.images[0] if object_info.entries.length == 1
64
+ end
65
+
66
+ def resource_id
67
+ object_info.ids[0] || @opts[:id] || @opts[:name] || ""
68
+ end
69
+
70
+ def to_s
71
+ name = @opts[:name] || @opts[:id]
72
+ "Podman Container #{name}"
73
+ end
74
+
75
+ private
76
+
77
+ def object_info
78
+ return @info if defined?(@info)
79
+
80
+ opts = @opts
81
+ @info = inspec.podman.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,108 @@
1
+ require "inspec/resources/command"
2
+ require_relative "docker_object"
3
+ require "inspec/utils/podman"
4
+
5
+ module Inspec::Resources
6
+ class PodmanImage < Inspec.resource(1)
7
+ include Inspec::Resources::DockerObject
8
+ include Inspec::Utils::Podman
9
+
10
+ name "podman_image"
11
+ supports platform: "unix"
12
+
13
+ desc "InSpec core resource to retrieve information about podman image"
14
+
15
+ example <<~EXAMPLE
16
+ describe podman_image("docker.io/library/busybox") do
17
+ it { should exist }
18
+ its("repo_tags") { should include "docker.io/library/busybox:latest" }
19
+ its("size") { should eq 1636053 }
20
+ its("resource_id") { should eq "docker.io/library/busybox:latest" }
21
+ end
22
+
23
+ describe podman_image("docker.io/library/busybox:latest") do
24
+ it { should exist }
25
+ end
26
+
27
+ describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do
28
+ it { should exist }
29
+ end
30
+
31
+ describe podman_image(id: "3c19bafed223") do
32
+ it { should exist }
33
+ end
34
+ EXAMPLE
35
+
36
+ attr_reader :opts, :image_info
37
+
38
+ def initialize(opts)
39
+ skip_resource "The `podman_image` resource is not yet available on your OS." unless inspec.os.unix?
40
+ opts = { image: opts } if opts.is_a?(String)
41
+ @opts = sanitize_options(opts)
42
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
43
+
44
+ @image_info = get_image_info
45
+ end
46
+
47
+ LABELS = {
48
+ "id" => "ID",
49
+ "repo_tags" => "RepoTags",
50
+ "size" => "Size",
51
+ "digest" => "Digest",
52
+ "created_at" => "Created",
53
+ "version" => "Version",
54
+ "names_history" => "NamesHistory",
55
+ "repo_digests" => "RepoDigests",
56
+ "architecture" => "Architecture",
57
+ "os" => "Os",
58
+ "virtual_size" => "VirtualSize",
59
+ }.freeze
60
+
61
+ ## This creates all the required properties methods dynamically.
62
+ LABELS.each do |k, v|
63
+ define_method(k) do
64
+ image_info[k.to_s]
65
+ end
66
+ end
67
+
68
+ def exist?
69
+ ! image_info.empty?
70
+ end
71
+
72
+ def resource_id
73
+ opts[:id] || opts[:image] || ""
74
+ end
75
+
76
+ def to_s
77
+ "podman_image #{resource_id}"
78
+ end
79
+
80
+ private
81
+
82
+ def sanitize_options(opts)
83
+ opts.merge!(parse_components_from_image(opts[:image]))
84
+
85
+ # assume a "latest" tag if we don't have one
86
+ opts[:tag] ||= "latest"
87
+
88
+ # Assemble/reassemble the image from the repo and tag
89
+ opts[:image] = "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
90
+
91
+ opts
92
+ end
93
+
94
+ def get_image_info
95
+ current_image = opts[:id] || opts[:image] || opts[:repo] + ":" + opts[:tag]
96
+ json_key_label = generate_go_template(LABELS)
97
+ podman_inspect_cmd = inspec.command("podman image inspect #{current_image} --format '{#{json_key_label}}'")
98
+
99
+ if podman_inspect_cmd.exit_status == 0
100
+ parse_command_output(podman_inspect_cmd.stdout)
101
+ elsif podman_inspect_cmd.stderr =~ /failed to find image/
102
+ {}
103
+ else
104
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman image information for #{current_image}.\nError message: #{podman_inspect_cmd.stderr}"
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,81 @@
1
+ require "inspec/resources/command"
2
+ require "inspec/utils/podman"
3
+ module Inspec::Resources
4
+ class PodmanNetwork < Inspec.resource(1)
5
+ include Inspec::Utils::Podman
6
+
7
+ name "podman_network"
8
+
9
+ supports platform: "unix"
10
+
11
+ desc "InSpec core resource to retrive information about the given Podman network"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_network("podman") do
15
+ it { should exist }
16
+ end
17
+ describe podman_network("3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f") do
18
+ its("driver") { should eq bridge }
19
+ end
20
+ EXAMPLE
21
+
22
+ LABELS = {
23
+ id: "ID",
24
+ name: "Name",
25
+ driver: "Driver",
26
+ labels: "Labels",
27
+ options: "Options",
28
+ ipam_options: "IPAMOptions",
29
+ internal: "Internal",
30
+ created: "Created",
31
+ ipv6_enabled: "IPv6Enabled",
32
+ dns_enabled: "DNSEnabled",
33
+ network_interface: "NetworkInterface",
34
+ subnets: "Subnets",
35
+ }.freeze
36
+
37
+ attr_reader :param, :network_info
38
+ def initialize(param)
39
+ skip_resource "The `podman_network` resource is not yet available on your OS." unless inspec.os.unix?
40
+
41
+ @param = param
42
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
43
+
44
+ @network_info = get_network_info
45
+ end
46
+
47
+ ## This creates all the required properties methods dynamically.
48
+ LABELS.each do |k, v|
49
+ define_method(k) do
50
+ network_info[k.to_s]
51
+ end
52
+ end
53
+
54
+ def exist?
55
+ !network_info.empty?
56
+ end
57
+
58
+ def resource_id
59
+ id || param || ""
60
+ end
61
+
62
+ def to_s
63
+ "podman_network #{resource_id}"
64
+ end
65
+
66
+ private
67
+
68
+ def get_network_info
69
+ go_template_format = generate_go_template(LABELS)
70
+ result = inspec.command("podman network inspect #{param} --format '{#{go_template_format}}'")
71
+
72
+ if result.exit_status == 0
73
+ parse_command_output(result.stdout)
74
+ elsif result.stderr =~ /network not found/
75
+ {}
76
+ else
77
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman network information for #{param}.\nError message: #{result.stderr}"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,101 @@
1
+ require "inspec/resources/command"
2
+ require "inspec/utils/podman"
3
+
4
+ module Inspec::Resources
5
+ class PodmanPod < Inspec.resource(1)
6
+ include Inspec::Utils::Podman
7
+
8
+ name "podman_pod"
9
+ supports platform: "unix"
10
+
11
+ desc "InSpec core resource to retrieve information about podman pod"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_pod("nginx-frontend") do
15
+ it { should exist }
16
+ its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
17
+ its("name") { should eq "nginx-frontend" }
18
+ its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
19
+ its("create_command") { should include "new:nginx-frontend" }
20
+ its("state") { should eq "Running" }
21
+ its("hostname") { should eq "" }
22
+ its("create_cgroup") { should eq true }
23
+ its("cgroup_parent") { should eq "user.slice" }
24
+ its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" }
25
+ its("create_infra") { should eq true }
26
+ its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" }
27
+ its("infra_config") { should include "DNSOption" }
28
+ its("shared_namespaces") { should include "ipc" }
29
+ its("num_containers") { should eq 2 }
30
+ its("containers") { should_not be nil }
31
+ end
32
+
33
+ describe podman_pod("non-existing-pod") do
34
+ it { should_not exist }
35
+ end
36
+ EXAMPLE
37
+
38
+ attr_reader :pod_info, :pod_id
39
+
40
+ def initialize(pod_id)
41
+ skip_resource "The `podman_pod` resource is not yet available on your OS." unless inspec.os.unix?
42
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
43
+
44
+ @pod_id = pod_id
45
+ @pod_info = get_pod_info
46
+ end
47
+
48
+ LABELS = {
49
+ "id" => "ID",
50
+ "name" => "Name",
51
+ "created_at" => "Created",
52
+ "create_command" => "CreateCommand",
53
+ "state" => "State",
54
+ "hostname" => "Hostname",
55
+ "create_cgroup" => "CreateCgroup",
56
+ "cgroup_parent" => "CgroupParent",
57
+ "cgroup_path" => "CgroupPath",
58
+ "create_infra" => "CreateInfra",
59
+ "infra_container_id" => "InfraContainerID",
60
+ "infra_config" => "InfraConfig",
61
+ "shared_namespaces" => "SharedNamespaces",
62
+ "num_containers" => "NumContainers",
63
+ "containers" => "Containers",
64
+ }.freeze
65
+
66
+ # This creates all the required properties methods dynamically.
67
+ LABELS.each do |k, _|
68
+ define_method(k) do
69
+ pod_info[k.to_s]
70
+ end
71
+ end
72
+
73
+ def exist?
74
+ !pod_info.empty?
75
+ end
76
+
77
+ def resource_id
78
+ pod_id
79
+ end
80
+
81
+ def to_s
82
+ "Podman Pod #{resource_id}"
83
+ end
84
+
85
+ private
86
+
87
+ def get_pod_info
88
+ json_key_label = generate_go_template(LABELS)
89
+
90
+ inspect_pod_cmd = inspec.command("podman pod inspect #{pod_id} --format '{#{json_key_label}}'")
91
+
92
+ if inspect_pod_cmd.exit_status == 0
93
+ parse_command_output(inspect_pod_cmd.stdout)
94
+ elsif inspect_pod_cmd.stderr =~ /no pod with name or ID/
95
+ {}
96
+ else
97
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman pod information for #{pod_id}.\nError message: #{inspect_pod_cmd.stderr}"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,87 @@
1
+ require "inspec/resources/command"
2
+ require "inspec/utils/podman"
3
+
4
+ module Inspec::Resources
5
+ class PodmanVolume < Inspec.resource(1)
6
+ include Inspec::Utils::Podman
7
+
8
+ name "podman_volume"
9
+ supports platform: "unix"
10
+
11
+ desc "InSpec core resource to retrieve information about podman volume"
12
+
13
+ example <<~EXAMPLE
14
+ describe podman_volume("my_volume") do
15
+ it { should exist }
16
+ its("name") { should eq "my_volume" }
17
+ its("driver") { should eq "local" }
18
+ its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
19
+ its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
20
+ its("labels") { should eq({}) }
21
+ its("scope") { should eq "local" }
22
+ its("options") { should eq({}) }
23
+ its("mount_count") { should eq 0 }
24
+ its("needs_copy_up") { should eq true }
25
+ its("needs_chown") { should eq true }
26
+ end
27
+ EXAMPLE
28
+
29
+ attr_reader :volume_info, :volume_name
30
+
31
+ def initialize(volume_name)
32
+ skip_resource "The `podman_volume` resource is not yet available on your OS." unless inspec.os.unix?
33
+ raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
34
+
35
+ @volume_name = volume_name
36
+ @volume_info = get_volume_info
37
+ end
38
+
39
+ LABELS = {
40
+ "name" => "Name",
41
+ "driver" => "Driver",
42
+ "mountpoint" => "Mountpoint",
43
+ "created_at" => "CreatedAt",
44
+ "labels" => "Labels",
45
+ "scope" => "Scope",
46
+ "options" => "Options",
47
+ "mount_count" => "MountCount",
48
+ "needs_copy_up" => "NeedsCopyUp",
49
+ "needs_chown" => "NeedsChown",
50
+ }.freeze
51
+
52
+ # This creates all the required properties methods dynamically.
53
+ LABELS.each do |k, _|
54
+ define_method(k) do
55
+ volume_info[k.to_s]
56
+ end
57
+ end
58
+
59
+ def exist?
60
+ !volume_info.empty?
61
+ end
62
+
63
+ def resource_id
64
+ volume_name
65
+ end
66
+
67
+ def to_s
68
+ "podman_volume #{resource_id}"
69
+ end
70
+
71
+ private
72
+
73
+ def get_volume_info
74
+ json_key_label = generate_go_template(LABELS)
75
+
76
+ inspect_volume_cmd = inspec.command("podman volume inspect #{volume_name} --format '{#{json_key_label}}'")
77
+
78
+ if inspect_volume_cmd.exit_status == 0
79
+ parse_command_output(inspect_volume_cmd.stdout)
80
+ elsif inspect_volume_cmd.stderr =~ /inspecting object: no such/
81
+ {}
82
+ else
83
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman volume information for #{volume_name}.\nError message: #{inspect_volume_cmd.stderr}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -81,7 +81,8 @@ module Inspec::Resources
81
81
  # Socket path and empty host in the connection string establishes socket connection
82
82
  # Socket connection only enabled for non-windows platforms
83
83
  # Windows does not support unix domain sockets
84
- "psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} -A -t -w -c #{escaped_query(query)}"
84
+ option_port = @port.nil? ? "" : "-p #{@port}" # add explicit port if specified
85
+ "psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} #{option_port} -A -t -w -c #{escaped_query(query)}"
85
86
  else
86
87
  # Host in connection string establishes tcp/ip connection
87
88
  if inspec.os.windows?
@@ -646,7 +646,7 @@ module Inspec::Resources
646
646
  return nil if srv.nil? || srv[0].nil?
647
647
 
648
648
  # extract values from service
649
- parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[0-9]+)\t(?<name>\S*)$/.match(srv[0])
649
+ parsed_srv = /^(?<pid>[0-9-]+)\t(?<exit>[\-0-9]+)\t(?<name>\S*)$/.match(srv[0])
650
650
  enabled = !parsed_srv["name"].nil? # it's in the list
651
651
 
652
652
  # check if the service is running
@@ -73,6 +73,7 @@ require "inspec/resources/mssql_sys_conf"
73
73
  require "inspec/resources/mysql"
74
74
  require "inspec/resources/mysql_conf"
75
75
  require "inspec/resources/mysql_session"
76
+ require "inspec/resources/nftables"
76
77
  require "inspec/resources/nginx"
77
78
  require "inspec/resources/nginx_conf"
78
79
  require "inspec/resources/npm"
data/lib/inspec/rule.rb CHANGED
@@ -8,13 +8,15 @@ require "inspec/impact"
8
8
  require "inspec/resource"
9
9
  require "inspec/resources/os"
10
10
  require "inspec/input_registry"
11
+ require "inspec/waiver_file_reader"
12
+ require "inspec/utils/convert"
11
13
 
12
14
  module Inspec
13
15
  class Rule
14
16
  include ::RSpec::Matchers
15
17
 
16
18
  attr_reader :__waiver_data
17
- attr_accessor :resource_dsl
19
+ attr_accessor :resource_dsl, :na_impact_freeze
18
20
  attr_reader :__profile_id
19
21
 
20
22
  def initialize(id, profile_id, resource_dsl, opts, &block)
@@ -38,6 +40,7 @@ module Inspec
38
40
  @__merge_count = 0
39
41
  @__merge_changes = []
40
42
  @__skip_only_if_eval = opts[:skip_only_if_eval]
43
+ @__na_rule = {}
41
44
 
42
45
  # evaluate the given definition
43
46
  return unless block_given?
@@ -73,10 +76,13 @@ module Inspec
73
76
  end
74
77
 
75
78
  def impact(v = nil)
76
- if v.is_a?(String)
77
- @impact = Inspec::Impact.impact_from_string(v)
78
- elsif !v.nil?
79
- @impact = v
79
+ # N/A impact freeze is required when only_applicable_if block has reset impact value to zero"
80
+ unless na_impact_freeze
81
+ if v.is_a?(String)
82
+ @impact = Inspec::Impact.impact_from_string(v)
83
+ elsif !v.nil?
84
+ @impact = v
85
+ end
80
86
  end
81
87
 
82
88
  @impact
@@ -133,15 +139,28 @@ module Inspec
133
139
  #
134
140
  # @param [Type] &block returns true if tests are added, false otherwise
135
141
  # @return [nil]
136
- def only_if(message = nil)
142
+ def only_if(message = nil, impact: nil)
137
143
  return unless block_given?
138
144
  return if @__skip_only_if_eval == true
139
145
 
146
+ self.impact(impact) if impact && !yield
140
147
  @__skip_rule[:result] ||= !yield
141
148
  @__skip_rule[:type] = :only_if
142
149
  @__skip_rule[:message] = message
143
150
  end
144
151
 
152
+ def only_applicable_if(message = nil)
153
+ return unless block_given?
154
+ return if yield
155
+
156
+ impact(0.0)
157
+ self.na_impact_freeze = true # this flag prevents impact value to reset to any other value
158
+
159
+ @__na_rule[:result] ||= !yield
160
+ @__na_rule[:type] = :only_applicable_if
161
+ @__na_rule[:message] = message
162
+ end
163
+
145
164
  # Describe will add one or more tests to this control. There is 2 ways
146
165
  # of calling it:
147
166
  #
@@ -252,6 +271,10 @@ module Inspec
252
271
  rule.instance_variable_get(:@__skip_rule)
253
272
  end
254
273
 
274
+ def self.na_status(rule)
275
+ rule.instance_variable_get(:@__na_rule)
276
+ end
277
+
255
278
  def self.set_skip_rule(rule, value, message = nil, type = :only_if)
256
279
  rule.instance_variable_set(:@__skip_rule,
257
280
  {
@@ -273,16 +296,26 @@ module Inspec
273
296
  # creates a dummay array of "checks" with a skip outcome
274
297
  def self.prepare_checks(rule)
275
298
  skip_check = skip_status(rule)
276
- return checks(rule) unless skip_check[:result].eql?(true)
299
+ na_check = na_status(rule)
300
+ return checks(rule) unless skip_check[:result].eql?(true) || na_check[:result].eql?(true)
277
301
 
278
- if skip_check[:message]
279
- msg = "Skipped control due to #{skip_check[:type]} condition: #{skip_check[:message]}"
302
+ resource = rule.noop
303
+ if skip_check[:result].eql?(true)
304
+ if skip_check[:message]
305
+ msg = "Skipped control due to #{skip_check[:type]} condition: #{skip_check[:message]}"
306
+ else
307
+ msg = "Skipped control due to #{skip_check[:type]} condition."
308
+ end
309
+ resource.skip_resource(msg)
280
310
  else
281
- msg = "Skipped control due to #{skip_check[:type]} condition."
311
+ if na_check[:message]
312
+ msg = "N/A control due to #{na_check[:type]} condition: #{na_check[:message]}"
313
+ else
314
+ msg = "N/A control due to #{na_check[:type]} condition."
315
+ end
316
+ resource.fail_resource(msg)
282
317
  end
283
318
 
284
- resource = rule.noop
285
- resource.skip_resource(msg)
286
319
  [["describe", [resource], nil]]
287
320
  end
288
321
 
@@ -337,17 +370,20 @@ module Inspec
337
370
  # only_if mechanism)
338
371
  # Double underscore: not intended to be called as part of the DSL
339
372
  def __apply_waivers
340
- input_name = @__rule_id # TODO: control ID slugging
341
- registry = Inspec::InputRegistry.instance
342
- input = registry.inputs_by_profile.dig(__profile_id, input_name)
343
- return unless input && input.has_value? && input.value.is_a?(Hash)
373
+ control_id = @__rule_id # TODO: control ID slugging
374
+ waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
375
+
376
+ waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil?
377
+
378
+ return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
344
379
 
345
380
  # An InSpec Input is a datastructure that tracks a profile parameter
346
381
  # over time. Its value can be set by many sources, and it keeps a
347
382
  # log of each "set" event so that when it is collapsed to a value,
348
383
  # it can determine the correct (highest priority) value.
349
384
  # Store in an instance variable for.. later reading???
350
- @__waiver_data = input.value
385
+ @__waiver_data = waiver_data_by_profile[control_id]
386
+
351
387
  __waiver_data["skipped_due_to_waiver"] = false
352
388
  __waiver_data["message"] = ""
353
389
 
@@ -376,6 +412,7 @@ module Inspec
376
412
  # expiration_date. We only care here if it has a "run" key and it
377
413
  # is false-like, since all non-skipped waiver operations are handled
378
414
  # during reporting phase.
415
+ __waiver_data["run"] = Converter.to_boolean(__waiver_data["run"]) if __waiver_data.key?("run")
379
416
  return unless __waiver_data.key?("run") && !__waiver_data["run"]
380
417
 
381
418
  # OK, apply a skip.
@@ -1,3 +1,5 @@
1
+ require "inspec/enhanced_outcomes"
2
+
1
3
  module Inspec
2
4
  class RunData
3
5
  Control = Struct.new(
@@ -31,6 +33,10 @@ module Inspec
31
33
  ].each do |field|
32
34
  self[field] = raw_ctl_data[field]
33
35
  end
36
+
37
+ def status
38
+ Inspec::EnhancedOutcomes.determine_status(results, impact)
39
+ end
34
40
  end
35
41
  end
36
42
 
@@ -16,14 +16,20 @@ module Inspec
16
16
  :total,
17
17
  :passed,
18
18
  :skipped,
19
- :failed
19
+ :failed,
20
+ :not_reviewed,
21
+ :not_applicable,
22
+ :error
20
23
  ) do
21
24
  include HashLikeStruct
22
25
  def initialize(raw_stat_ctl_data)
23
26
  self.total = raw_stat_ctl_data[:total]
24
27
  self.passed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:passed][:total])
25
- self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total])
26
28
  self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total])
29
+ self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total]) if raw_stat_ctl_data[:skipped]
30
+ self.not_reviewed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_reviewed][:total]) if raw_stat_ctl_data[:not_reviewed]
31
+ self.not_applicable = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_applicable][:total]) if raw_stat_ctl_data[:not_applicable]
32
+ self.error = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:error][:total]) if raw_stat_ctl_data[:error]
27
33
  end
28
34
  end
29
35
  class Controls