inspec-core 5.22.65 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +24 -32
  4. data/etc/features.sig +6 -0
  5. data/etc/features.yaml +94 -0
  6. data/inspec-core.gemspec +15 -14
  7. data/lib/inspec/backend.rb +2 -0
  8. data/lib/inspec/base_cli.rb +80 -4
  9. data/lib/inspec/cached_fetcher.rb +24 -3
  10. data/lib/inspec/cli.rb +292 -235
  11. data/lib/inspec/config.rb +24 -11
  12. data/lib/inspec/dependencies/cache.rb +33 -0
  13. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  14. data/lib/inspec/dsl.rb +1 -1
  15. data/lib/inspec/enhanced_outcomes.rb +1 -0
  16. data/lib/inspec/errors.rb +5 -0
  17. data/lib/inspec/exceptions.rb +2 -0
  18. data/lib/inspec/feature/config.rb +75 -0
  19. data/lib/inspec/feature/runner.rb +26 -0
  20. data/lib/inspec/feature.rb +34 -0
  21. data/lib/inspec/fetcher/git.rb +5 -0
  22. data/lib/inspec/fetcher/url.rb +7 -29
  23. data/lib/inspec/globals.rb +6 -0
  24. data/lib/inspec/input_registry.rb +1 -5
  25. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  26. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  27. data/lib/inspec/profile.rb +46 -3
  28. data/lib/inspec/reporters/cli.rb +1 -1
  29. data/lib/inspec/reporters.rb +67 -54
  30. data/lib/inspec/resources/nftables.rb +1 -14
  31. data/lib/inspec/resources/oracledb_session.rb +3 -9
  32. data/lib/inspec/resources/postgres_session.rb +1 -1
  33. data/lib/inspec/resources/sybase_session.rb +2 -11
  34. data/lib/inspec/resources/virtualization.rb +1 -1
  35. data/lib/inspec/rule.rb +9 -14
  36. data/lib/inspec/run_data.rb +7 -5
  37. data/lib/inspec/runner.rb +35 -6
  38. data/lib/inspec/runner_rspec.rb +12 -9
  39. data/lib/inspec/secrets/yaml.rb +9 -3
  40. data/lib/inspec/shell.rb +10 -0
  41. data/lib/inspec/ui.rb +4 -0
  42. data/lib/inspec/utils/licensing_config.rb +9 -0
  43. data/lib/inspec/utils/profile_ast_helpers.rb +2 -1
  44. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  45. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  46. data/lib/inspec/version.rb +1 -1
  47. data/lib/inspec/waiver_file_reader.rb +68 -27
  48. data/lib/inspec.rb +2 -1
  49. data/lib/matchers/matchers.rb +3 -3
  50. data/lib/plugins/inspec-compliance/README.md +1 -11
  51. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -170
  52. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  53. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  54. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  55. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  56. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  57. data/lib/plugins/inspec-license/README.md +16 -0
  58. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  59. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  60. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  61. data/lib/plugins/inspec-parallel/README.md +27 -0
  62. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  63. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  64. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  65. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  66. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  67. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  68. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  69. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  70. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  71. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  72. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  73. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +10 -11
  74. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  75. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  76. data/lib/source_readers/inspec.rb +1 -1
  77. metadata +45 -19
@@ -27,33 +27,35 @@ module InspecPlugins
27
27
  option :copyright, type: :string, default: nil, desc: "A copyright statement, to be added to LICENSE"
28
28
 
29
29
  def plugin(plugin_name)
30
- plugin_type = determine_plugin_type(plugin_name)
31
- snake_case = plugin_name.tr("-", "_")
30
+ Inspec.with_feature("inspec-cli-init-plugin") {
31
+ plugin_type = determine_plugin_type(plugin_name)
32
+ snake_case = plugin_name.tr("-", "_")
32
33
 
33
- # Handle deprecation of option --hook
34
- unless options[:hook].nil?
35
- Inspec.deprecate "cli_option_hook"
36
- options[:activator] = options.delete(:hook)
37
- end
34
+ # Handle deprecation of option --hook
35
+ unless options[:hook].nil?
36
+ Inspec.deprecate "cli_option_hook"
37
+ options[:activator] = options.delete(:hook)
38
+ end
38
39
 
39
- template_vars = {
40
- name: plugin_name,
41
- plugin_name: plugin_name,
42
- snake_case: snake_case,
43
- }.merge(plugin_vars_from_opts)
40
+ template_vars = {
41
+ name: plugin_name,
42
+ plugin_name: plugin_name,
43
+ snake_case: snake_case,
44
+ }.merge(plugin_vars_from_opts)
44
45
 
45
- template_path = File.join("plugins", plugin_type + "-plugin-template")
46
+ template_path = File.join("plugins", plugin_type + "-plugin-template")
46
47
 
47
- render_opts = {
48
- templates_path: TEMPLATES_PATH,
49
- overwrite: options[:overwrite],
50
- file_rename_map: make_rename_map(plugin_type, plugin_name, snake_case),
51
- skip_files: make_skip_list(template_vars["activators"].keys),
52
- }
48
+ render_opts = {
49
+ templates_path: TEMPLATES_PATH,
50
+ overwrite: options[:overwrite],
51
+ file_rename_map: make_rename_map(plugin_type, plugin_name, snake_case),
52
+ skip_files: make_skip_list(template_vars["activators"].keys),
53
+ }
53
54
 
54
- renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
55
+ renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
55
56
 
56
- renderer.render_with_values(template_path, plugin_type + " plugin", template_vars)
57
+ renderer.render_with_values(template_path, plugin_type + " plugin", template_vars)
58
+ }
57
59
  end
58
60
 
59
61
  private
@@ -25,22 +25,24 @@ module InspecPlugins
25
25
  option :overwrite, type: :boolean, default: false,
26
26
  desc: "Overwrites existing directory"
27
27
  def profile(new_profile_name)
28
- unless valid_profile_platforms.include?(options[:platform])
29
- ui.error "Unable to generate profile: No template available for platform '#{options[:platform]}' (expected one of: #{valid_profile_platforms.join(", ")})"
30
- ui.exit(:usage_error)
31
- end
32
- template_path = File.join("profiles", options[:platform])
28
+ Inspec.with_feature("inspec-cli-init-profile") {
29
+ unless valid_profile_platforms.include?(options[:platform])
30
+ ui.error "Unable to generate profile: No template available for platform '#{options[:platform]}' (expected one of: #{valid_profile_platforms.join(", ")})"
31
+ ui.exit(:usage_error)
32
+ end
33
+ template_path = File.join("profiles", options[:platform])
33
34
 
34
- render_opts = {
35
- templates_path: TEMPLATES_PATH,
36
- overwrite: options[:overwrite],
37
- }
38
- renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
35
+ render_opts = {
36
+ templates_path: TEMPLATES_PATH,
37
+ overwrite: options[:overwrite],
38
+ }
39
+ renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
39
40
 
40
- vars = {
41
- name: new_profile_name,
41
+ vars = {
42
+ name: new_profile_name,
43
+ }
44
+ renderer.render_with_values(template_path, "profile", vars)
42
45
  }
43
- renderer.render_with_values(template_path, "profile", vars)
44
46
  end
45
47
  end
46
48
  end
@@ -35,21 +35,23 @@ module InspecPlugins
35
35
  # + Add --overwrite option
36
36
 
37
37
  def resource(resource_name)
38
- resource_vars_from_opts_resource
39
- template_vars = {
40
- name: options[:path], # This is used for the path prefix
41
- resource_name: resource_name,
42
- }
43
- template_vars.merge!(options)
44
- template_path = File.join("resources", template_vars["template"])
38
+ Inspec.with_feature("inspec-cli-init-resource") {
39
+ resource_vars_from_opts_resource
40
+ template_vars = {
41
+ name: options[:path], # This is used for the path prefix
42
+ resource_name: resource_name,
43
+ }
44
+ template_vars.merge!(options)
45
+ template_path = File.join("resources", template_vars["template"])
45
46
 
46
- render_opts = {
47
- templates_path: TEMPLATES_PATH,
48
- overwrite: options[:overwrite],
49
- file_rename_map: make_rename_map_resource(template_vars),
47
+ render_opts = {
48
+ templates_path: TEMPLATES_PATH,
49
+ overwrite: options[:overwrite],
50
+ file_rename_map: make_rename_map_resource(template_vars),
51
+ }
52
+ renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
53
+ renderer.render_with_values(template_path, "resource", template_vars)
50
54
  }
51
- renderer = InspecPlugins::Init::Renderer.new(ui, render_opts)
52
- renderer.render_with_values(template_path, "resource", template_vars)
53
55
  end
54
56
 
55
57
  private
@@ -0,0 +1,16 @@
1
+ # License Plugin
2
+
3
+ ## license list
4
+
5
+ Implements the `inspec license list` CLI command.
6
+
7
+ ## license add
8
+
9
+ Implements the `inspec license add` CLI command.
10
+
11
+ ### What This Plugin Does
12
+
13
+ This plugin consists of the following subcommands:
14
+
15
+ 1. `add`: helps to add a new license
16
+ 2. `list`: helps to list all the licenses for the current user
@@ -0,0 +1,6 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "inspec-license"
3
+ spec.summary = "Plugin to list user licenses."
4
+ spec.description = ""
5
+ spec.license = "Apache-2.0"
6
+ end
@@ -0,0 +1,26 @@
1
+ require "chef-licensing"
2
+ module InspecPlugins::License
3
+ class CLI < Inspec.plugin(2, :cli_command)
4
+ include Inspec::Dist
5
+
6
+ subcommand_desc "license SUBCOMMAND [options]", "Manage #{PRODUCT_NAME} license"
7
+ desc "list", "List licenses (not applicable to local licensing service)"
8
+ def list
9
+ ChefLicensing.list_license_keys_info
10
+ rescue ChefLicensing::Error => e
11
+ Inspec::Log.error e.message
12
+ Inspec::UI.new.exit(Inspec::UI::EXIT_LICENSE_NOT_SET)
13
+ end
14
+
15
+ desc "add", "Add a new license (not applicable to local licensing service)"
16
+ def add
17
+ ChefLicensing.add_license
18
+ rescue ChefLicensing::LicenseKeyFetcher::LicenseKeyAddNotAllowed => e
19
+ Inspec::Log.error e.message
20
+ Inspec::UI.new.exit(Inspec::UI::EXIT_LICENSE_NOT_SET)
21
+ rescue ChefLicensing::Error => e
22
+ Inspec::Log.error e.message
23
+ Inspec::UI.new.exit(Inspec::UI::EXIT_LICENSE_NOT_SET)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ module InspecPlugins
2
+ module License
3
+ class Plugin < ::Inspec.plugin(2)
4
+ plugin_name :"inspec-license"
5
+
6
+ if Inspec::Dist::EXEC_NAME == "inspec"
7
+ cli_command :license do
8
+ require_relative "inspec-license/cli"
9
+ InspecPlugins::License::CLI
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ # Parallel Plugin
2
+
3
+ Plugin to handle parallel InSpec scan operations over multiple targets.
4
+
5
+ ## parallel cli_command
6
+
7
+ Implements the `inspec parallel exec` CLI command.
8
+
9
+ ## child-status Plugin
10
+
11
+ This reporter is an InSpec Streaming Reporter. It is used internally by inspec parallel to provide status updates on child processes.
12
+
13
+ ### What This Plugin Does
14
+
15
+ For each control executed, after it is complete, the plugin emits a line to STDOUT like:
16
+ ```
17
+ 12/P/24/Control Title Here
18
+ ```
19
+ When the run is complete, the single line 'EOF_MARKER' is emitted.
20
+
21
+ Where:
22
+
23
+ - 12 is the number of the control (12th seen out of all controls in all profiles)
24
+ - P indicates that it Passed (Also F = Failed, S = Skipped, E = Errored)
25
+ - 24 is the total number of controls in the run
26
+ - "Control Title Here" is the title (or if title is missing, id) of the last executed control
27
+
@@ -0,0 +1,6 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "inspec-parallel"
3
+ spec.summary = "Plugin to handle parallel InSpec scan operations over multiple targets"
4
+ spec.description = ""
5
+ spec.license = "Apache-2.0"
6
+ end
@@ -0,0 +1,61 @@
1
+ module InspecPlugins::Parallelism
2
+ class StreamingReporter < Inspec.plugin(2, :streaming_reporter)
3
+ # Registering these methods with RSpec::Core::Formatters class is mandatory
4
+ RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending, :close
5
+
6
+ def initialize(output)
7
+ @status_mapping = {}
8
+ @control_counter = 0
9
+ initialize_streaming_reporter
10
+ end
11
+
12
+ def example_passed(notification)
13
+ set_example(notification, "passed")
14
+ end
15
+
16
+ def example_failed(notification)
17
+ set_example(notification, "failed")
18
+ end
19
+
20
+ def example_pending(notification)
21
+ set_example(notification, "skipped")
22
+ end
23
+
24
+ def close(notification)
25
+ # HACK: if we've reached the end of the execution, send a special marker, to ease EOF detection on Windows
26
+ puts "EOF_MARKER"
27
+ end
28
+
29
+ private
30
+
31
+ def set_example(notification, status)
32
+ control_id = notification.example.metadata[:id]
33
+ title = notification.example.metadata[:title]
34
+ set_status_mapping(control_id, status)
35
+ output_status(control_id, title) if control_ended?(notification, control_id)
36
+ end
37
+
38
+ def output_status(control_id, title)
39
+ @control_counter += 1
40
+ stat = @status_mapping[control_id]
41
+ stat = if stat.include?("failed")
42
+ "F"
43
+ else
44
+ if stat.include?("skipped")
45
+ "S"
46
+ else
47
+ stat.include?("passed") ? "P" : "E"
48
+ end
49
+ end
50
+ display_name = title.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8) if title
51
+ display_name = control_id.to_s.lstrip.force_encoding(Encoding::UTF_8) unless title
52
+
53
+ puts "#{@control_counter}/#{stat}/#{controls_count}/#{display_name}"
54
+ end
55
+
56
+ def set_status_mapping(control_id, status)
57
+ @status_mapping[control_id] ||= []
58
+ @status_mapping[control_id].push(status)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "command"
2
+ require "inspec/dist"
3
+ require "inspec/base_cli"
4
+ require "inspec/feature"
5
+
6
+ module InspecPlugins::Parallelism
7
+ class CLI < Inspec.plugin(2, :cli_command)
8
+ include Inspec::Dist
9
+
10
+ subcommand_desc "parallel SUBCOMMAND [options]", "Runs #{PRODUCT_NAME} operations parallely"
11
+
12
+ desc "exec", "Executes profile parallely"
13
+ option :option_file, aliases: :o, type: :string, required: true,
14
+ desc: "File that contains list of option strings"
15
+ option :dry_run, type: :boolean,
16
+ desc: "Print commands that will run"
17
+ option :verbose, type: :boolean,
18
+ desc: "Prints all thor options on dry run"
19
+ option :jobs, aliases: :j, type: :numeric,
20
+ desc: "Number of jobs to run parallely"
21
+ option :ui, type: :string, default: "status",
22
+ desc: "Which UI to use: status, text, silent"
23
+ option :bg, type: :boolean,
24
+ desc: "Runs parallel processes in background"
25
+ option :log_path, type: :string,
26
+ desc: "Path to the runner and error logs"
27
+ exec_options
28
+ def exec(default_profile = nil)
29
+ Inspec.with_feature("inspec-cli-parallel-exec") {
30
+ parallel_cmd = InspecPlugins::Parallelism::Command.new(options, default_profile)
31
+ if options[:dry_run]
32
+ parallel_cmd.dry_run
33
+ else
34
+ parallel_cmd.run
35
+ end
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,219 @@
1
+ require_relative "runner"
2
+ require_relative "validator"
3
+ require "erb" unless defined?(Erb)
4
+
5
+ module InspecPlugins
6
+ module Parallelism
7
+
8
+ class OptionFileNotReadable < RuntimeError
9
+ end
10
+
11
+ class Command
12
+ attr_accessor :cli_options_to_parallel_cmd, :default_profile, :sub_cmd, :invocations, :run_in_background
13
+
14
+ def initialize(cli_options_to_parallel_cmd, default_profile, sub_cmd = "exec")
15
+ @default_profile = default_profile
16
+ @cli_options_to_parallel_cmd = cli_options_to_parallel_cmd
17
+ @sub_cmd = sub_cmd
18
+ @logger = Inspec::Log
19
+ @invocations = read_options_file
20
+ @run_in_background = cli_options_to_parallel_cmd["bg"]
21
+ end
22
+
23
+ def run
24
+ validate_thor_options
25
+ validate_invocations!
26
+ runner = Runner.new(invocations, cli_options_to_parallel_cmd, sub_cmd)
27
+ catch_ctl_c_and_exit(runner) unless run_in_background
28
+ runner.run
29
+ end
30
+
31
+ def dry_run
32
+ validate_invocations!
33
+ dry_run_commands
34
+ end
35
+
36
+ private
37
+
38
+ def catch_ctl_c_and_exit(runner)
39
+ puts "Press CTL+C to stop\n"
40
+ trap("SIGINT") do
41
+ puts "\n"
42
+ puts "Shutting down jobs..."
43
+ if Inspec.locally_windows?
44
+ runner.kill_child_processes
45
+ sleep 1
46
+ puts "Renaming error log files..."
47
+ runner.rename_error_log_files
48
+ end
49
+ exit Inspec::UI::EXIT_TERMINATED_BY_CTL_C
50
+ end
51
+ end
52
+
53
+ def validate_thor_options
54
+ # only log path validation needed for now
55
+ validate_log_path
56
+ end
57
+
58
+ def validate_log_path
59
+ error, message = Validator.new(invocations, cli_options_to_parallel_cmd, sub_cmd).validate_log_path
60
+ if error
61
+ @logger.error message
62
+ Inspec::UI.new.exit(:usage_error)
63
+ end
64
+ end
65
+
66
+ def validate_invocations!
67
+ # Validation logic stays in Validator class...
68
+ Validator.new(invocations, cli_options_to_parallel_cmd, sub_cmd).validate
69
+ # UI logic stays in Command class.
70
+ valid = true
71
+ invocations.each do |invocation_data|
72
+ invocation_data[:validation_errors].each do |error_message|
73
+ valid = false
74
+ @logger.error "Line #{invocation_data[:line_no]}: " + error_message
75
+ end
76
+ end
77
+ unless valid
78
+ @logger.error "Please fix the options to proceed further."
79
+ Inspec::UI.new.exit(:usage_error)
80
+ end
81
+ end
82
+
83
+ def dry_run_commands
84
+ invocations.each do |invocation_data|
85
+ puts "inspec #{sub_cmd} #{invocation_data[:value]}"
86
+ end
87
+ end
88
+
89
+ ## Utility functions
90
+
91
+ def read_options_file
92
+ opts = []
93
+ begin
94
+ content = content_from_file(cli_options_to_parallel_cmd[:option_file])
95
+ rescue OptionFileNotReadable => e
96
+ @logger.error "Cannot read options file: #{e.message}"
97
+ Inspec::UI.new.exit(:usage_error)
98
+ end
99
+ content.each.with_index(1) do |str, index|
100
+ data_hash = { line_no: index }
101
+ str = ERB.new(str).result_with_hash(pid: "CHILD_PID").strip
102
+ str_has_comment = str.start_with?("#")
103
+ next if str.empty? || str_has_comment
104
+
105
+ default_options = fetch_default_options(str.split(" ")).lstrip
106
+ if str.start_with?("-")
107
+ data_hash[:value] = "#{default_profile} #{str} #{default_options}"
108
+ else
109
+ data_hash[:value] = "#{str} #{default_options}"
110
+ end
111
+ opts << data_hash
112
+ end
113
+ opts
114
+ end
115
+
116
+ def content_from_file(option_file)
117
+ if File.exist?(option_file)
118
+ unless [".sh", ".csh", ".ps1"].include? File.extname(option_file)
119
+ File.readlines(option_file)
120
+ else
121
+ if Inspec.locally_windows? && (File.extname(option_file) == ".ps1")
122
+ begin
123
+ output = `powershell -File "#{option_file}"`
124
+ output.split("\n")
125
+ rescue StandardError => e
126
+ raise OptionFileNotReadable.new("Error reading powershell file #{option_file}: #{e.message}")
127
+ end
128
+ elsif [".sh", ".csh"].include? File.extname(option_file)
129
+ begin
130
+ output = `bash "#{option_file}"`
131
+ output.split("\n")
132
+ rescue StandardError => e
133
+ raise OptionFileNotReadable.new("Error reading shell file #{option_file}: #{e.message}")
134
+ end
135
+ else
136
+ raise OptionFileNotReadable.new("Powershell not supported in your system.")
137
+ end
138
+ end
139
+ else
140
+ raise OptionFileNotReadable.new("Option file not found.")
141
+ end
142
+ end
143
+
144
+ # this must return empty string or default option string which are not part of option file
145
+ def fetch_default_options(option_line)
146
+ option_line = option_line.select { |word| word.start_with?("-") }
147
+
148
+ # remove prefixes from the options to compare with default options
149
+ option_line.map! do |option_key|
150
+ option_key.gsub(options_prefix(option_key), "").gsub("-", "_")
151
+ end
152
+
153
+ default_opts = ""
154
+ # iterate through the parallel cli default options and append the option and value which are not present in option file
155
+ parallel_cmd_default_cli_options.each do |cmd|
156
+ if cmd.is_a? String
157
+ append_default_value(default_opts, cmd) unless option_line.include?(cmd)
158
+ elsif cmd.is_a? Array
159
+ if !option_line.include?(cmd[0]) && !option_line.include?(cmd[1])
160
+ append_default_value(default_opts, cmd[0])
161
+ end
162
+ end
163
+ end
164
+ default_opts
165
+ end
166
+
167
+ # returns array of default options of the subcommand
168
+ def parallel_cmd_default_cli_options
169
+ sub_cmd_opts = Inspec::InspecCLI.commands[sub_cmd].options
170
+ parallel_cmd_default_opts = cli_options_to_parallel_cmd.keys & sub_cmd_opts.keys.map(&:to_s)
171
+ options_to_append = parallel_cmd_default_opts
172
+
173
+ if cli_options_to_parallel_cmd["dry_run"] && !cli_options_to_parallel_cmd["verbose"]
174
+ # to not show thor default options of inspec commands in dry run
175
+ sub_cmd_opts_with_defaults = fetch_sub_cmd_default_options(sub_cmd_opts)
176
+ options_to_append -= sub_cmd_opts_with_defaults
177
+ end
178
+
179
+ default_opts_to_append = []
180
+
181
+ # append the options and its aliases if available.
182
+ options_to_append.each do |option_name|
183
+ opt_alias = sub_cmd_opts[option_name.to_sym].aliases
184
+ if opt_alias.empty?
185
+ default_opts_to_append << option_name
186
+ else
187
+ default_opts_to_append << [option_name, opt_alias[0].to_s]
188
+ end
189
+ end
190
+ default_opts_to_append
191
+ end
192
+
193
+ def append_default_value(default_opts, command_name)
194
+ default_value = cli_options_to_parallel_cmd[command_name.to_sym]
195
+ default_value = default_value.join(" ") if default_value.is_a? Array
196
+ default_opts << " --#{command_name.gsub("_", "-")} #{default_value}"
197
+ end
198
+
199
+ def options_prefix(option_name)
200
+ if option_name.start_with?("--")
201
+ option_name.start_with?("--no-") ? "--no-" : "--"
202
+ else
203
+ "-"
204
+ end
205
+ end
206
+
207
+ def fetch_sub_cmd_default_options(sub_cmd_opts)
208
+ default_options_to_remove = []
209
+ sub_cmd_opts_with_defaults = sub_cmd_opts.select { |_, c| !c.default.nil? }.keys.map(&:to_s)
210
+ sub_cmd_opts_with_defaults.each do |default_opt_name|
211
+ if sub_cmd_opts[default_opt_name.to_sym].default == cli_options_to_parallel_cmd[default_opt_name]
212
+ default_options_to_remove << default_opt_name
213
+ end
214
+ end
215
+ default_options_to_remove
216
+ end
217
+ end
218
+ end
219
+ end