inspec 0.20.1 → 0.21.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -2
- data/docs/dsl_inspec.rst +2 -2
- data/docs/resources.rst +9 -9
- data/docs/ruby_usage.rst +145 -0
- data/inspec.gemspec +1 -0
- data/lib/bundles/inspec-compliance/cli.rb +15 -2
- data/lib/inspec/cli.rb +23 -10
- data/lib/inspec/dsl.rb +0 -52
- data/lib/inspec/objects/or_test.rb +1 -0
- data/lib/inspec/objects/test.rb +4 -4
- data/lib/inspec/profile.rb +76 -61
- data/lib/inspec/profile_context.rb +12 -11
- data/lib/inspec/rspec_json_formatter.rb +93 -40
- data/lib/inspec/rule.rb +7 -29
- data/lib/inspec/runner.rb +15 -4
- data/lib/inspec/runner_mock.rb +1 -1
- data/lib/inspec/runner_rspec.rb +26 -24
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +3 -3
- data/lib/resources/auditd_rules.rb +2 -2
- data/lib/resources/host.rb +1 -1
- data/lib/resources/interface.rb +1 -1
- data/lib/resources/kernel_parameter.rb +1 -1
- data/lib/resources/mount.rb +2 -1
- data/lib/resources/mysql_session.rb +1 -1
- data/lib/resources/os_env.rb +2 -2
- data/lib/resources/passwd.rb +33 -93
- data/lib/resources/port.rb +47 -3
- data/lib/resources/processes.rb +3 -3
- data/lib/resources/service.rb +33 -1
- data/lib/resources/user.rb +15 -15
- data/lib/utils/base_cli.rb +1 -3
- data/lib/utils/filter.rb +30 -7
- data/test/cookbooks/os_prepare/recipes/_upstart_service_centos.rb +4 -0
- data/test/functional/helper.rb +1 -0
- data/test/functional/inheritance_test.rb +1 -1
- data/test/functional/inspec_compliance_test.rb +4 -3
- data/test/functional/inspec_exec_json_test.rb +122 -0
- data/test/functional/inspec_exec_test.rb +23 -117
- data/test/functional/{inspec_json_test.rb → inspec_json_profile_test.rb} +13 -15
- data/test/functional/inspec_test.rb +15 -2
- data/test/helper.rb +5 -1
- data/test/integration/default/auditd_rules_spec.rb +3 -3
- data/test/integration/default/kernel_parameter_spec.rb +6 -6
- data/test/integration/default/service_spec.rb +4 -0
- data/test/resource/command_test.rb +9 -9
- data/test/resource/dsl_test.rb +1 -1
- data/test/resource/file_test.rb +17 -17
- data/test/unit/control_test.rb +1 -1
- data/test/unit/mock/cmd/hpux-netstat-inet +10 -0
- data/test/unit/mock/cmd/hpux-netstat-inet6 +11 -0
- data/test/unit/mock/profiles/skippy-profile-os/controls/one.rb +1 -1
- data/test/unit/profile_context_test.rb +2 -2
- data/test/unit/profile_test.rb +11 -14
- data/test/unit/resources/passwd_test.rb +13 -14
- data/test/unit/resources/port_test.rb +14 -0
- data/test/unit/resources/processes_test.rb +3 -3
- data/test/unit/resources/service_test.rb +103 -39
- data/test/unit/utils/filter_table_test.rb +35 -3
- metadata +25 -4
    
        data/lib/resources/port.rb
    CHANGED
    
    | @@ -3,7 +3,6 @@ | |
| 3 3 | 
             
            # author: Dominik Richter
         | 
| 4 4 |  | 
| 5 5 | 
             
            require 'utils/parser'
         | 
| 6 | 
            -
             | 
| 7 6 | 
             
            # Usage:
         | 
| 8 7 | 
             
            # describe port(80) do
         | 
| 9 8 | 
             
            #   it { should be_listening }
         | 
| @@ -47,6 +46,8 @@ module Inspec::Resources | |
| 47 46 | 
             
                    @port_manager = FreeBsdPorts.new(inspec)
         | 
| 48 47 | 
             
                  elsif os.solaris?
         | 
| 49 48 | 
             
                    @port_manager = SolarisPorts.new(inspec)
         | 
| 49 | 
            +
                  elsif os.hpux?
         | 
| 50 | 
            +
                    @port_manager = HpuxPorts.new(inspec)
         | 
| 50 51 | 
             
                  else
         | 
| 51 52 | 
             
                    return skip_resource 'The `port` resource is not supported on your OS yet.'
         | 
| 52 53 | 
             
                  end
         | 
| @@ -292,7 +293,6 @@ module Inspec::Resources | |
| 292 293 | 
             
                  # parse each line
         | 
| 293 294 | 
             
                  # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name
         | 
| 294 295 | 
             
                  parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)\s+(\S+)\s+(\S+)/.match(line)
         | 
| 295 | 
            -
             | 
| 296 296 | 
             
                  return {} if parsed.nil? || line.match(/^proto/i)
         | 
| 297 297 |  | 
| 298 298 | 
             
                  # parse ip4 and ip6 addresses
         | 
| @@ -408,7 +408,7 @@ module Inspec::Resources | |
| 408 408 | 
             
                  # parse the content
         | 
| 409 409 | 
             
                  netstat_ports = parse_netstat(cmd.stdout)
         | 
| 410 410 |  | 
| 411 | 
            -
                  # filter all ports, where we listen
         | 
| 411 | 
            +
                  # filter all ports, where we `listen`
         | 
| 412 412 | 
             
                  listen = netstat_ports.select { |val|
         | 
| 413 413 | 
             
                    !val['state'].nil? && 'listen'.casecmp(val['state']) == 0
         | 
| 414 414 | 
             
                  }
         | 
| @@ -433,4 +433,48 @@ module Inspec::Resources | |
| 433 433 | 
             
                  ports
         | 
| 434 434 | 
             
                end
         | 
| 435 435 | 
             
              end
         | 
| 436 | 
            +
             | 
| 437 | 
            +
              # extracts information from netstat for hpux
         | 
| 438 | 
            +
              class HpuxPorts < FreeBsdPorts
         | 
| 439 | 
            +
                def info
         | 
| 440 | 
            +
                  ## Can't use 'netstat -an -f inet -f inet6' as the latter -f option overrides the former one and return only inet ports
         | 
| 441 | 
            +
                  cmd1 = inspec.command('netstat -an -f inet')
         | 
| 442 | 
            +
                  return nil if cmd1.exit_status.to_i != 0
         | 
| 443 | 
            +
                  cmd2 = inspec.command('netstat -an -f inet6')
         | 
| 444 | 
            +
                  return nil if cmd2.exit_status.to_i != 0
         | 
| 445 | 
            +
                  cmd = cmd1.stdout + cmd2.stdout
         | 
| 446 | 
            +
                  ports = []
         | 
| 447 | 
            +
                  # parse all lines
         | 
| 448 | 
            +
                  cmd.each_line do |line|
         | 
| 449 | 
            +
                    port_info = parse_netstat_line(line)
         | 
| 450 | 
            +
                    next unless %w{tcp tcp6 udp udp6}.include?(port_info[:protocol])
         | 
| 451 | 
            +
                    ports.push(port_info)
         | 
| 452 | 
            +
                  end
         | 
| 453 | 
            +
                  # select all ports, where we `listen`
         | 
| 454 | 
            +
                  ports.select { |val| val if 'listen'.casecmp(val[:state]) == 0 }
         | 
| 455 | 
            +
                end
         | 
| 456 | 
            +
             | 
| 457 | 
            +
                def parse_netstat_line(line)
         | 
| 458 | 
            +
                  # parse each line
         | 
| 459 | 
            +
                  # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - (state)
         | 
| 460 | 
            +
                  parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?/.match(line)
         | 
| 461 | 
            +
             | 
| 462 | 
            +
                  return {} if parsed.nil? || line.match(/^proto/i) || line.match(/^active/i)
         | 
| 463 | 
            +
                  protocol = parsed[1].downcase
         | 
| 464 | 
            +
                  state = parsed[6].nil?? ' ' : parsed[6].downcase
         | 
| 465 | 
            +
                  local_addr = parsed[4]
         | 
| 466 | 
            +
                  local_addr[local_addr.rindex('.')] = ':'
         | 
| 467 | 
            +
                  # extract host and port information
         | 
| 468 | 
            +
                  host, port = parse_net_address(local_addr, protocol)
         | 
| 469 | 
            +
                  # map data
         | 
| 470 | 
            +
                  {
         | 
| 471 | 
            +
                    port: port,
         | 
| 472 | 
            +
                    address: host,
         | 
| 473 | 
            +
                    protocol: protocol,
         | 
| 474 | 
            +
                    state: state,
         | 
| 475 | 
            +
                    process: nil,
         | 
| 476 | 
            +
                    pid: nil,
         | 
| 477 | 
            +
                  }
         | 
| 478 | 
            +
                end
         | 
| 479 | 
            +
              end
         | 
| 436 480 | 
             
            end
         | 
    
        data/lib/resources/processes.rb
    CHANGED
    
    
    
        data/lib/resources/service.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ | |
| 3 3 | 
             
            # author: Dominik Richter
         | 
| 4 4 | 
             
            # author: Stephan Renatus
         | 
| 5 5 | 
             
            # license: All rights reserved
         | 
| 6 | 
            +
            require 'hashie'
         | 
| 6 7 |  | 
| 7 8 | 
             
            module Inspec::Resources
         | 
| 8 9 | 
             
              class Runlevels < Hash
         | 
| @@ -67,7 +68,7 @@ module Inspec::Resources | |
| 67 68 | 
             
              # Ubuntu < 15.04 : upstart
         | 
| 68 69 | 
             
              #
         | 
| 69 70 | 
             
              # TODO: extend the logic to detect the running init system, independently of OS
         | 
| 70 | 
            -
              class Service < Inspec.resource(1)
         | 
| 71 | 
            +
              class Service < Inspec.resource(1) # rubocop:disable ClassLength
         | 
| 71 72 | 
             
                name 'service'
         | 
| 72 73 | 
             
                desc 'Use the service InSpec audit resource to test if the named service is installed, running and/or enabled.'
         | 
| 73 74 | 
             
                example "
         | 
| @@ -75,11 +76,16 @@ module Inspec::Resources | |
| 75 76 | 
             
                    it { should be_installed }
         | 
| 76 77 | 
             
                    it { should be_enabled }
         | 
| 77 78 | 
             
                    it { should be_running }
         | 
| 79 | 
            +
                    its('type') { should be 'systemd' }
         | 
| 78 80 | 
             
                  end
         | 
| 79 81 |  | 
| 80 82 | 
             
                  describe service('service_name').runlevels(3, 5) do
         | 
| 81 83 | 
             
                    it { should be_enabled }
         | 
| 82 84 | 
             
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  describe service('service_name').params do
         | 
| 87 | 
            +
                    its('UnitFileState') { should eq 'enabled' }
         | 
| 88 | 
            +
                  end
         | 
| 83 89 | 
             
                "
         | 
| 84 90 |  | 
| 85 91 | 
             
                attr_reader :service_ctl
         | 
| @@ -163,6 +169,11 @@ module Inspec::Resources | |
| 163 169 | 
             
                  info[:enabled]
         | 
| 164 170 | 
             
                end
         | 
| 165 171 |  | 
| 172 | 
            +
                def params
         | 
| 173 | 
            +
                  return {} if info.nil?
         | 
| 174 | 
            +
                  Hashie::Mash.new(info[:params] || {})
         | 
| 175 | 
            +
                end
         | 
| 176 | 
            +
             | 
| 166 177 | 
             
                # verifies the service is registered
         | 
| 167 178 | 
             
                def installed?(_name = nil, _version = nil)
         | 
| 168 179 | 
             
                  return false if info.nil?
         | 
| @@ -181,9 +192,29 @@ module Inspec::Resources | |
| 181 192 | 
             
                  Runlevels.from_hash(self, info[:runlevels], args)
         | 
| 182 193 | 
             
                end
         | 
| 183 194 |  | 
| 195 | 
            +
                # returns the service type from info
         | 
| 196 | 
            +
                def type
         | 
| 197 | 
            +
                  return nil if info.nil?
         | 
| 198 | 
            +
                  info[:type]
         | 
| 199 | 
            +
                end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                # returns the service name from info
         | 
| 202 | 
            +
                def name
         | 
| 203 | 
            +
                  return @service_name if info.nil?
         | 
| 204 | 
            +
                  info[:name]
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                # returns the service description from info
         | 
| 208 | 
            +
                def description
         | 
| 209 | 
            +
                  return nil if info.nil?
         | 
| 210 | 
            +
                  info[:description]
         | 
| 211 | 
            +
                end
         | 
| 212 | 
            +
             | 
| 184 213 | 
             
                def to_s
         | 
| 185 214 | 
             
                  "Service #{@service_name}"
         | 
| 186 215 | 
             
                end
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                private :info
         | 
| 187 218 | 
             
              end
         | 
| 188 219 |  | 
| 189 220 | 
             
              class ServiceManager
         | 
| @@ -229,6 +260,7 @@ module Inspec::Resources | |
| 229 260 | 
             
                    running: running,
         | 
| 230 261 | 
             
                    enabled: enabled,
         | 
| 231 262 | 
             
                    type: 'systemd',
         | 
| 263 | 
            +
                    params: params,
         | 
| 232 264 | 
             
                  }
         | 
| 233 265 | 
             
                end
         | 
| 234 266 | 
             
              end
         | 
    
        data/lib/resources/user.rb
    CHANGED
    
    | @@ -6,15 +6,15 @@ | |
| 6 6 | 
             
            #
         | 
| 7 7 | 
             
            # describe user('root') do
         | 
| 8 8 | 
             
            #   it { should exist }
         | 
| 9 | 
            -
            #   its( | 
| 10 | 
            -
            #   its( | 
| 11 | 
            -
            #   its( | 
| 12 | 
            -
            #   its( | 
| 13 | 
            -
            #   its( | 
| 14 | 
            -
            #   its( | 
| 15 | 
            -
            #   its( | 
| 16 | 
            -
            #   its( | 
| 17 | 
            -
            #   its( | 
| 9 | 
            +
            #   its('uid') { should eq 0 }
         | 
| 10 | 
            +
            #   its('gid') { should eq 0 }
         | 
| 11 | 
            +
            #   its('group') { should eq 'root' }
         | 
| 12 | 
            +
            #   its('groups') { should eq ['root', 'wheel']}
         | 
| 13 | 
            +
            #   its('home') { should eq '/root' }
         | 
| 14 | 
            +
            #   its('shell') { should eq '/bin/bash' }
         | 
| 15 | 
            +
            #   its('mindays') { should eq 0 }
         | 
| 16 | 
            +
            #   its('maxdays') { should eq 99 }
         | 
| 17 | 
            +
            #   its('warndays') { should eq 5 }
         | 
| 18 18 | 
             
            # end
         | 
| 19 19 | 
             
            #
         | 
| 20 20 | 
             
            # The following  Serverspec  matchers are deprecated in favor for direct value access
         | 
| @@ -24,8 +24,8 @@ | |
| 24 24 | 
             
            #   it { should have_uid 0 }
         | 
| 25 25 | 
             
            #   it { should have_home_directory '/root' }
         | 
| 26 26 | 
             
            #   it { should have_login_shell '/bin/bash' }
         | 
| 27 | 
            -
            #   its( | 
| 28 | 
            -
            #   its( | 
| 27 | 
            +
            #   its('minimum_days_between_password_change') { should eq 0 }
         | 
| 28 | 
            +
            #   its('maximum_days_between_password_change') { should eq 99 }
         | 
| 29 29 | 
             
            # end
         | 
| 30 30 |  | 
| 31 31 | 
             
            # ServerSpec tests that are not supported:
         | 
| @@ -119,13 +119,13 @@ module Inspec::Resources | |
| 119 119 |  | 
| 120 120 | 
             
                # implement 'mindays' method to be compatible with serverspec
         | 
| 121 121 | 
             
                def minimum_days_between_password_change
         | 
| 122 | 
            -
                  deprecated('minimum_days_between_password_change', "Please use  | 
| 122 | 
            +
                  deprecated('minimum_days_between_password_change', "Please use: its('mindays')")
         | 
| 123 123 | 
             
                  mindays
         | 
| 124 124 | 
             
                end
         | 
| 125 125 |  | 
| 126 126 | 
             
                # implement 'maxdays' method to be compatible with serverspec
         | 
| 127 127 | 
             
                def maximum_days_between_password_change
         | 
| 128 | 
            -
                  deprecated('maximum_days_between_password_change', "Please use  | 
| 128 | 
            +
                  deprecated('maximum_days_between_password_change', "Please use: its('maxdays')")
         | 
| 129 129 | 
             
                  maxdays
         | 
| 130 130 | 
             
                end
         | 
| 131 131 |  | 
| @@ -137,12 +137,12 @@ module Inspec::Resources | |
| 137 137 | 
             
                end
         | 
| 138 138 |  | 
| 139 139 | 
             
                def has_home_directory?(compare_home)
         | 
| 140 | 
            -
                  deprecated('has_home_directory?', "Please use  | 
| 140 | 
            +
                  deprecated('has_home_directory?', "Please use: its('home')")
         | 
| 141 141 | 
             
                  home == compare_home
         | 
| 142 142 | 
             
                end
         | 
| 143 143 |  | 
| 144 144 | 
             
                def has_login_shell?(compare_shell)
         | 
| 145 | 
            -
                  deprecated('has_login_shell?', "Please use  | 
| 145 | 
            +
                  deprecated('has_login_shell?', "Please use: its('shell')")
         | 
| 146 146 | 
             
                  shell == compare_shell
         | 
| 147 147 | 
             
                end
         | 
| 148 148 |  | 
    
        data/lib/utils/base_cli.rb
    CHANGED
    
    | @@ -45,8 +45,6 @@ module Inspec | |
| 45 45 | 
             
                end
         | 
| 46 46 |  | 
| 47 47 | 
             
                def self.exec_options
         | 
| 48 | 
            -
                  option :id, type: :string,
         | 
| 49 | 
            -
                    desc: 'Attach a profile ID to all test results'
         | 
| 50 48 | 
             
                  target_options
         | 
| 51 49 | 
             
                  profile_options
         | 
| 52 50 | 
             
                  option :controls, type: :array,
         | 
| @@ -68,7 +66,7 @@ module Inspec | |
| 68 66 | 
             
                  runner = Inspec::Runner.new(o)
         | 
| 69 67 | 
             
                  targets.each { |target| runner.add_target(target, opts) }
         | 
| 70 68 | 
             
                  exit runner.run
         | 
| 71 | 
            -
                rescue RuntimeError => e
         | 
| 69 | 
            +
                rescue RuntimeError, Train::UserError => e
         | 
| 72 70 | 
             
                  $stderr.puts e.message
         | 
| 73 71 | 
             
                  exit 1
         | 
| 74 72 | 
             
                end
         | 
    
        data/lib/utils/filter.rb
    CHANGED
    
    | @@ -95,15 +95,38 @@ module FilterTable | |
| 95 95 |  | 
| 96 96 | 
             
                private
         | 
| 97 97 |  | 
| 98 | 
            +
                def matches_float(x, y)
         | 
| 99 | 
            +
                  return false if x.nil?
         | 
| 100 | 
            +
                  return false if !x.is_a?(Float) && (x =~ /\A[-+]?(\d+\.?\d*|\.\d+)\z/).nil?
         | 
| 101 | 
            +
                  x.to_f == y
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def matches_int(x, y)
         | 
| 105 | 
            +
                  return false if x.nil?
         | 
| 106 | 
            +
                  return false if !x.is_a?(Integer) && (x =~ /\A[-+]?\d+\z/).nil?
         | 
| 107 | 
            +
                  x.to_i == y
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def matches_regex(x, y)
         | 
| 111 | 
            +
                  return x == y if x.is_a?(Regexp)
         | 
| 112 | 
            +
                  !x.to_s.match(y).nil?
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def matches(x, y)
         | 
| 116 | 
            +
                  x === y # rubocop:disable Style/CaseEquality
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
             | 
| 98 119 | 
             
                def filter_lines(table, field, condition)
         | 
| 120 | 
            +
                  m = case condition
         | 
| 121 | 
            +
                      when Float   then method(:matches_float)
         | 
| 122 | 
            +
                      when Integer then method(:matches_int)
         | 
| 123 | 
            +
                      when Regexp  then method(:matches_regex)
         | 
| 124 | 
            +
                      else              method(:matches)
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
             | 
| 99 127 | 
             
                  table.find_all do |line|
         | 
| 100 128 | 
             
                    next unless line.key?(field)
         | 
| 101 | 
            -
                     | 
| 102 | 
            -
                    when condition
         | 
| 103 | 
            -
                      true
         | 
| 104 | 
            -
                    else
         | 
| 105 | 
            -
                      false
         | 
| 106 | 
            -
                    end
         | 
| 129 | 
            +
                    m.call(line[field], condition)
         | 
| 107 130 | 
             
                  end
         | 
| 108 131 | 
             
                end
         | 
| 109 132 | 
             
              end
         | 
| @@ -134,7 +157,7 @@ module FilterTable | |
| 134 157 | 
             
                    fields.each do |method, field_name|
         | 
| 135 158 | 
             
                      block = blocks[method]
         | 
| 136 159 | 
             
                      define_method method.to_sym do |condition = Show, &cond_block|
         | 
| 137 | 
            -
                        return block.call(self) unless block.nil?
         | 
| 160 | 
            +
                        return block.call(self, condition) unless block.nil?
         | 
| 138 161 | 
             
                        return where(nil).get_fields(field_name) if condition == Show && !block_given?
         | 
| 139 162 | 
             
                        where({ field_name => condition }, &cond_block)
         | 
| 140 163 | 
             
                      end
         | 
    
        data/test/functional/helper.rb
    CHANGED
    
    | @@ -20,6 +20,7 @@ module FunctionalHelper | |
| 20 20 | 
             
              let(:examples_path) { File.join(repo_path, 'examples') }
         | 
| 21 21 |  | 
| 22 22 | 
             
              let(:example_profile) { File.join(examples_path, 'profile') }
         | 
| 23 | 
            +
              let(:example_control) { File.join(example_profile, 'controls', 'example.rb') }
         | 
| 23 24 | 
             
              let(:inheritance_profile) { File.join(examples_path, 'profile') }
         | 
| 24 25 |  | 
| 25 26 | 
             
              let(:dst) {
         | 
| @@ -37,7 +37,8 @@ describe 'inspec compliance' do | |
| 37 37 |  | 
| 38 38 | 
             
              it 'inspec compliance profiles without authentication' do
         | 
| 39 39 | 
             
                out = inspec('compliance profile')
         | 
| 40 | 
            -
                out. | 
| 40 | 
            +
                out.stdout.must_include 'You need to login first with `inspec compliance login`'
         | 
| 41 | 
            +
                out.exit_status.must_equal 0
         | 
| 41 42 | 
             
              end
         | 
| 42 43 |  | 
| 43 44 | 
             
              it 'try to upload a profile without directory' do
         | 
| @@ -48,8 +49,8 @@ describe 'inspec compliance' do | |
| 48 49 |  | 
| 49 50 | 
             
              it 'try to upload a profile a non-existing path' do
         | 
| 50 51 | 
             
                out = inspec('compliance upload /path/to/dir')
         | 
| 51 | 
            -
                out.stdout.must_include ' | 
| 52 | 
            -
                out.exit_status.must_equal  | 
| 52 | 
            +
                out.stdout.must_include 'You need to login first with `inspec compliance login`'
         | 
| 53 | 
            +
                out.exit_status.must_equal 0
         | 
| 53 54 | 
             
              end
         | 
| 54 55 |  | 
| 55 56 | 
             
              it 'logout' do
         | 
| @@ -0,0 +1,122 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # author: Dominik Richter
         | 
| 3 | 
            +
            # author: Christoph Hartmann
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'functional/helper'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            describe 'inspec exec with json formatter' do
         | 
| 8 | 
            +
              include FunctionalHelper
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              it 'can execute a simple file with the json formatter' do
         | 
| 11 | 
            +
                out = inspec('exec ' + example_control + ' --format json')
         | 
| 12 | 
            +
                out.stderr.must_equal ''
         | 
| 13 | 
            +
                out.exit_status.must_equal 0
         | 
| 14 | 
            +
                JSON.load(out.stdout).must_be_kind_of Hash
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              it 'can execute the profile with the json formatter' do
         | 
| 18 | 
            +
                out = inspec('exec ' + example_profile + ' --format json')
         | 
| 19 | 
            +
                out.stderr.must_equal ''
         | 
| 20 | 
            +
                out.exit_status.must_equal 0
         | 
| 21 | 
            +
                JSON.load(out.stdout).must_be_kind_of Hash
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              describe 'execute a profile with json formatting' do
         | 
| 25 | 
            +
                let(:json) { JSON.load(inspec('exec ' + example_profile + ' --format json').stdout) }
         | 
| 26 | 
            +
                let(:profile) { json['profiles']['profile'] }
         | 
| 27 | 
            +
                let(:controls) { profile['controls'] }
         | 
| 28 | 
            +
                let(:ex1) { controls['tmp-1.0'] }
         | 
| 29 | 
            +
                let(:ex2) {
         | 
| 30 | 
            +
                  k = controls.keys.find { |x| x =~ /generated/ }
         | 
| 31 | 
            +
                  controls[k]
         | 
| 32 | 
            +
                }
         | 
| 33 | 
            +
                let(:ex3) { profile['controls']['gordon-1.0'] }
         | 
| 34 | 
            +
                let(:check_result) {
         | 
| 35 | 
            +
                  ex3['results'].find { |x| x['resource'] == 'gordon_config' }
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                it 'has all the metadata' do
         | 
| 39 | 
            +
                  actual = profile.dup
         | 
| 40 | 
            +
                  key = actual.delete('controls').keys
         | 
| 41 | 
            +
                              .find { |x| x =~ /generated from example.rb/ }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  actual.must_equal({
         | 
| 44 | 
            +
                    "name" => "profile",
         | 
| 45 | 
            +
                    "title" => "InSpec Example Profile",
         | 
| 46 | 
            +
                    "maintainer" => "Chef Software, Inc.",
         | 
| 47 | 
            +
                    "copyright" => "Chef Software, Inc.",
         | 
| 48 | 
            +
                    "copyright_email" => "support@chef.io",
         | 
| 49 | 
            +
                    "license" => "Apache 2 license",
         | 
| 50 | 
            +
                    "summary" => "Demonstrates the use of InSpec Compliance Profile",
         | 
| 51 | 
            +
                    "version" => "1.0.0",
         | 
| 52 | 
            +
                    "supports" => [{"os-family" => "unix"}],
         | 
| 53 | 
            +
                    "groups" => {
         | 
| 54 | 
            +
                      "controls/meta.rb" => {"title"=>"SSH Server Configuration", "controls"=>["ssh-1"]},
         | 
| 55 | 
            +
                      "controls/example.rb" => {"title"=>"/tmp profile", "controls"=>["tmp-1.0", key]},
         | 
| 56 | 
            +
                      "controls/gordon.rb" => {"title"=>"Gordon Config Checks", "controls"=>["gordon-1.0"]},
         | 
| 57 | 
            +
                    },
         | 
| 58 | 
            +
                  })
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                it 'must have 4 controls' do
         | 
| 62 | 
            +
                  controls.length.must_equal 4
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                it 'has an id for every control' do
         | 
| 66 | 
            +
                  controls.keys.find(&:nil?).must_be :nil?
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                it 'has no missing checks' do
         | 
| 70 | 
            +
                  json['other_checks'].must_equal([])
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                it 'has results for every control' do
         | 
| 74 | 
            +
                  ex1['results'].length.must_equal 1
         | 
| 75 | 
            +
                  ex2['results'].length.must_equal 1
         | 
| 76 | 
            +
                  ex3['results'].length.must_equal 2
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                it 'has the right result for tmp-1.0' do
         | 
| 80 | 
            +
                  actual = ex1.dup
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  src = actual.delete('source_location')
         | 
| 83 | 
            +
                  src[0].must_match %r{examples/profile/controls/example.rb$}
         | 
| 84 | 
            +
                  src[1].must_equal 8
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  result = actual.delete('results')[0]
         | 
| 87 | 
            +
                  result.wont_be :nil?
         | 
| 88 | 
            +
                  result['status'].must_equal 'passed'
         | 
| 89 | 
            +
                  result['code_desc'].must_equal 'File /tmp should be directory'
         | 
| 90 | 
            +
                  result['run_time'].wont_be :nil?
         | 
| 91 | 
            +
                  result['start_time'].wont_be :nil?
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  actual.must_equal({
         | 
| 94 | 
            +
                    "title" => "Create /tmp directory",
         | 
| 95 | 
            +
                    "desc" => "An optional description...",
         | 
| 96 | 
            +
                    "impact" => 0.7,
         | 
| 97 | 
            +
                    "refs" => [
         | 
| 98 | 
            +
                      {
         | 
| 99 | 
            +
                        "url" => "http://...",
         | 
| 100 | 
            +
                        "ref" => "Document A-12"
         | 
| 101 | 
            +
                      }
         | 
| 102 | 
            +
                    ],
         | 
| 103 | 
            +
                    "tags" => {
         | 
| 104 | 
            +
                      "data" => "temp data",
         | 
| 105 | 
            +
                      "security" => nil
         | 
| 106 | 
            +
                    },
         | 
| 107 | 
            +
                    "code" => "control \"tmp-1.0\" do                        # A unique ID for this control\n  impact 0.7                                # The criticality, if this control fails.\n  title \"Create /tmp directory\"             # A human-readable title\n  desc \"An optional description...\"         # Describe why this is needed\n  tag data: \"temp data\"                     # A tag allows you to associate key information\n  tag \"security\"                            # to the test\n  ref \"Document A-12\", url: 'http://...'    # Additional references\n\n  describe file('/tmp') do                  # The actual test\n    it { should be_directory }\n  end\nend\n",
         | 
| 108 | 
            +
                  })
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
              describe 'with a profile that is not supported on this OS/platform' do
         | 
| 113 | 
            +
                let(:out) { inspec('exec ' + File.join(profile_path, 'skippy-profile-os') + ' --format json') }
         | 
| 114 | 
            +
                let(:json) { JSON.load(out.stdout) }
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                # TODO: failure handling in json formatters...
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                it 'never runs the actual resource' do
         | 
| 119 | 
            +
                  File.exist?('/tmp/inspec_test_DONT_CREATE').must_equal false
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
              end
         | 
| 122 | 
            +
            end
         |