lib/inspec/shell.rb in inspec-2.1.21 vs lib/inspec/shell.rb in inspec-2.1.30
- old
+ new
@@ -1,220 +1,220 @@
-# encoding: utf-8
-# author: Dominik Richter
-# author: Christoph Hartmann
-
-require 'pry'
-
-module Inspec
- # A pry based shell for inspec. Given a runner (with a configured backend and
- # all that jazz), this shell will produce a pry shell from which you can run
- # inspec/ruby commands that will be run within the context of the runner.
- class Shell
- def initialize(runner)
- @runner = runner
- end
-
- def start
- # This will hold a single evaluation binding context as opened within
- # the instance_eval context of the anonymous class that the profile
- # context creates to evaluate each individual test file. We want to
- # pretend like we are constantly appending to the same file and want
- # to capture the local variable context from inside said class.
- @ctx_binding = @runner.eval_with_virtual_profile('binding')
- configure_pry
- @ctx_binding.pry
- end
-
- def configure_pry # rubocop:disable Metrics/AbcSize
- # Delete any before_session, before_eval, and after_eval hooks so we can
- # replace them with our own. Pry 0.10 used to have a single method to clear
- # all hooks, but this was removed in Pry 0.11.
- [:before_session, :before_eval, :after_eval].each do |event|
- Pry.hooks.get_hooks(event).keys.map { |hook| Pry.hooks.delete_hook(event, hook) }
- end
-
- that = self
-
- # Add the help command
- Pry::Commands.block_command 'help', 'Show examples' do |resource|
- that.help(resource)
- end
-
- # configure pry shell prompt
- Pry.config.prompt_name = 'inspec'
- Pry.prompt = [proc { "#{readline_ignore("\e[1m\e[32m")}#{Pry.config.prompt_name}> #{readline_ignore("\e[0m")}" }]
-
- # Add a help menu as the default intro
- Pry.hooks.add_hook(:before_session, 'inspec_intro') do
- intro
- print_target_info
- end
-
- # Track the rules currently registered and what their merge count is.
- Pry.hooks.add_hook(:before_eval, 'inspec_before_eval') do
- @runner.reset
- end
-
- # After pry has evaluated a commanding within the binding context of a
- # test file, register all the rules it discovered.
- Pry.hooks.add_hook(:after_eval, 'inspec_after_eval') do
- @runner.load
- @runner.run_tests if !@runner.all_rules.empty?
- end
-
- # Don't print out control class inspection when the user uses DSL methods.
- # Instead produce a result of evaluating their control.
- Pry.config.print = proc do |_output_, value, pry|
- next if !@runner.all_rules.empty?
- pry.pager.open do |pager|
- pager.print pry.config.output_prefix
- Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
- end
- end
- end
-
- def readline_ignore(code)
- "\001#{code}\002"
- end
-
- def mark(x)
- "\e[1m\e[39m#{x}\e[0m"
- end
-
- def print_example(example)
- # determine min whitespace that can be removed
- min = nil
- example.lines.each do |line|
- if !line.strip.empty? # ignore empty lines
- line_whitespace = line.length - line.lstrip.length
- min = line_whitespace if min.nil? || line_whitespace < min
- end
- end
- # remove whitespace from each line
- example.gsub(/\n\s{#{min}}/, "\n")
- end
-
- def intro
- puts 'Welcome to the interactive InSpec Shell'
- puts "To find out how to use it, type: #{mark 'help'}"
- puts
- end
-
- def print_target_info
- ctx = @runner.backend
- puts <<~EOF
- You are currently running on:
-
- #{Inspec::BaseCLI.detect(params: ctx.platform.params, indent: 4, color: 39)}
- EOF
- end
-
- def help(topic = nil)
- if topic.nil?
-
- puts <<~EOF
- Available commands:
-
- `[resource]` - run resource on target machine
- `help resources` - show all available resources that can be used as commands
- `help [resource]` - information about a specific resource
- `help matchers` - show information about common matchers
- `exit` - exit the InSpec shell
-
- You can use resources in this environment to test the target machine. For example:
-
- command('uname -a').stdout
- file('/proc/cpuinfo').content => "value"
-
- #{print_target_info}
- EOF
- elsif topic == 'resources'
- resources.sort.each do |resource|
- puts " - #{resource}"
- end
- elsif topic == 'matchers'
- print_matchers_help
- elsif !Inspec::Resource.registry[topic].nil?
- topic_info = Inspec::Resource.registry[topic]
- info = "#{mark 'Name:'} #{topic}\n\n"
- unless topic_info.desc.nil?
- info += "#{mark 'Description:'}\n\n"
- info += "#{topic_info.desc}\n\n"
- end
-
- unless topic_info.example.nil?
- info += "#{mark 'Example:'}\n"
- info += "#{print_example(topic_info.example)}\n\n"
- end
-
- info += "#{mark 'Web Reference:'}\n\n"
- info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n"
- puts info
- else
- puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
- end
- end
-
- def resources
- Inspec::Resource.registry.keys
- end
-
- def print_matchers_help
- puts <<~EOL
- Matchers are used to compare resource values to expectations. While some
- resources implement their own custom matchers, the following matchers are
- common amongst all resources:
-
- #{mark 'be'}
-
- The #{mark 'be'} matcher can be used to compare numeric values.
-
- its('size') { should be >= 10 }
-
- #{mark 'cmp'}
-
- The #{mark 'cmp'} matcher is like #{mark 'eq'} but less restrictive. It will try
- to fit the resource value to the expectation.
-
- "Protocol" likely returns a string, but cmp will ensure it's a number before
- comparing:
-
- its('Protocol') { should cmp 2 }
- its('Protocol') { should cmp '2' }
-
- "users" may return an array, but if it contains only one item, cmp will compare
- it as a string or number as needed:
-
- its('users') { should cmp 'root' }
-
- cmp is not case-sensitive:
-
- its('log_format') { should cmp 'raw' }
- its('log_format') { should cmp 'RAW' }
-
- #{mark 'eq'}
-
- The #{mark 'eq'} matcher tests for exact equality of two values. Value type
- (string, number, etc.) is important and must be the same. For a less-restrictive
- comparison matcher, use the #{mark 'cmp'} matcher.
-
- its('RSAAuthentication') { should_not eq 'no' }
-
- #{mark 'include'}
-
- The #{mark 'include'} matcher tests to see if a value is included in a list.
-
- its('users') { should include 'my_user' }
-
- #{mark 'match'}
-
- The #{mark 'match'} matcher can be used to test a string for a match using a
- regular expression.
-
- its('content') { should_not match /^MyKey:\\s+some value/ }
-
- For more examples, see: https://www.inspec.io/docs/reference/matchers/
-
- EOL
- end
- end
-end
+# encoding: utf-8
+# author: Dominik Richter
+# author: Christoph Hartmann
+
+require 'pry'
+
+module Inspec
+ # A pry based shell for inspec. Given a runner (with a configured backend and
+ # all that jazz), this shell will produce a pry shell from which you can run
+ # inspec/ruby commands that will be run within the context of the runner.
+ class Shell
+ def initialize(runner)
+ @runner = runner
+ end
+
+ def start
+ # This will hold a single evaluation binding context as opened within
+ # the instance_eval context of the anonymous class that the profile
+ # context creates to evaluate each individual test file. We want to
+ # pretend like we are constantly appending to the same file and want
+ # to capture the local variable context from inside said class.
+ @ctx_binding = @runner.eval_with_virtual_profile('binding')
+ configure_pry
+ @ctx_binding.pry
+ end
+
+ def configure_pry # rubocop:disable Metrics/AbcSize
+ # Delete any before_session, before_eval, and after_eval hooks so we can
+ # replace them with our own. Pry 0.10 used to have a single method to clear
+ # all hooks, but this was removed in Pry 0.11.
+ [:before_session, :before_eval, :after_eval].each do |event|
+ Pry.hooks.get_hooks(event).keys.map { |hook| Pry.hooks.delete_hook(event, hook) }
+ end
+
+ that = self
+
+ # Add the help command
+ Pry::Commands.block_command 'help', 'Show examples' do |resource|
+ that.help(resource)
+ end
+
+ # configure pry shell prompt
+ Pry.config.prompt_name = 'inspec'
+ Pry.prompt = [proc { "#{readline_ignore("\e[1m\e[32m")}#{Pry.config.prompt_name}> #{readline_ignore("\e[0m")}" }]
+
+ # Add a help menu as the default intro
+ Pry.hooks.add_hook(:before_session, 'inspec_intro') do
+ intro
+ print_target_info
+ end
+
+ # Track the rules currently registered and what their merge count is.
+ Pry.hooks.add_hook(:before_eval, 'inspec_before_eval') do
+ @runner.reset
+ end
+
+ # After pry has evaluated a commanding within the binding context of a
+ # test file, register all the rules it discovered.
+ Pry.hooks.add_hook(:after_eval, 'inspec_after_eval') do
+ @runner.load
+ @runner.run_tests if !@runner.all_rules.empty?
+ end
+
+ # Don't print out control class inspection when the user uses DSL methods.
+ # Instead produce a result of evaluating their control.
+ Pry.config.print = proc do |_output_, value, pry|
+ next if !@runner.all_rules.empty?
+ pry.pager.open do |pager|
+ pager.print pry.config.output_prefix
+ Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
+ end
+ end
+ end
+
+ def readline_ignore(code)
+ "\001#{code}\002"
+ end
+
+ def mark(x)
+ "\e[1m\e[39m#{x}\e[0m"
+ end
+
+ def print_example(example)
+ # determine min whitespace that can be removed
+ min = nil
+ example.lines.each do |line|
+ if !line.strip.empty? # ignore empty lines
+ line_whitespace = line.length - line.lstrip.length
+ min = line_whitespace if min.nil? || line_whitespace < min
+ end
+ end
+ # remove whitespace from each line
+ example.gsub(/\n\s{#{min}}/, "\n")
+ end
+
+ def intro
+ puts 'Welcome to the interactive InSpec Shell'
+ puts "To find out how to use it, type: #{mark 'help'}"
+ puts
+ end
+
+ def print_target_info
+ ctx = @runner.backend
+ puts <<~EOF
+ You are currently running on:
+
+ #{Inspec::BaseCLI.detect(params: ctx.platform.params, indent: 4, color: 39)}
+ EOF
+ end
+
+ def help(topic = nil)
+ if topic.nil?
+
+ puts <<~EOF
+ Available commands:
+
+ `[resource]` - run resource on target machine
+ `help resources` - show all available resources that can be used as commands
+ `help [resource]` - information about a specific resource
+ `help matchers` - show information about common matchers
+ `exit` - exit the InSpec shell
+
+ You can use resources in this environment to test the target machine. For example:
+
+ command('uname -a').stdout
+ file('/proc/cpuinfo').content => "value"
+
+ #{print_target_info}
+ EOF
+ elsif topic == 'resources'
+ resources.sort.each do |resource|
+ puts " - #{resource}"
+ end
+ elsif topic == 'matchers'
+ print_matchers_help
+ elsif !Inspec::Resource.registry[topic].nil?
+ topic_info = Inspec::Resource.registry[topic]
+ info = "#{mark 'Name:'} #{topic}\n\n"
+ unless topic_info.desc.nil?
+ info += "#{mark 'Description:'}\n\n"
+ info += "#{topic_info.desc}\n\n"
+ end
+
+ unless topic_info.example.nil?
+ info += "#{mark 'Example:'}\n"
+ info += "#{print_example(topic_info.example)}\n\n"
+ end
+
+ info += "#{mark 'Web Reference:'}\n\n"
+ info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n"
+ puts info
+ else
+ puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
+ end
+ end
+
+ def resources
+ Inspec::Resource.registry.keys
+ end
+
+ def print_matchers_help
+ puts <<~EOL
+ Matchers are used to compare resource values to expectations. While some
+ resources implement their own custom matchers, the following matchers are
+ common amongst all resources:
+
+ #{mark 'be'}
+
+ The #{mark 'be'} matcher can be used to compare numeric values.
+
+ its('size') { should be >= 10 }
+
+ #{mark 'cmp'}
+
+ The #{mark 'cmp'} matcher is like #{mark 'eq'} but less restrictive. It will try
+ to fit the resource value to the expectation.
+
+ "Protocol" likely returns a string, but cmp will ensure it's a number before
+ comparing:
+
+ its('Protocol') { should cmp 2 }
+ its('Protocol') { should cmp '2' }
+
+ "users" may return an array, but if it contains only one item, cmp will compare
+ it as a string or number as needed:
+
+ its('users') { should cmp 'root' }
+
+ cmp is not case-sensitive:
+
+ its('log_format') { should cmp 'raw' }
+ its('log_format') { should cmp 'RAW' }
+
+ #{mark 'eq'}
+
+ The #{mark 'eq'} matcher tests for exact equality of two values. Value type
+ (string, number, etc.) is important and must be the same. For a less-restrictive
+ comparison matcher, use the #{mark 'cmp'} matcher.
+
+ its('RSAAuthentication') { should_not eq 'no' }
+
+ #{mark 'include'}
+
+ The #{mark 'include'} matcher tests to see if a value is included in a list.
+
+ its('users') { should include 'my_user' }
+
+ #{mark 'match'}
+
+ The #{mark 'match'} matcher can be used to test a string for a match using a
+ regular expression.
+
+ its('content') { should_not match /^MyKey:\\s+some value/ }
+
+ For more examples, see: https://www.inspec.io/docs/reference/matchers/
+
+ EOL
+ end
+ end
+end