inspec 1.14.1 → 1.15.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 +38 -2
- data/docs/resources/crontab.md.erb +62 -0
- data/examples/inheritance/inspec.lock +2 -2
- data/examples/meta-profile/inspec.lock +1 -1
- data/examples/meta-profile/vendor/9ad48391d4e6efff0a13d06736c5b075fb021410e0a629e087bc21e9617d957c.tar.gz +0 -0
- data/inspec.gemspec +1 -0
- data/lib/bundles/inspec-habitat.rb +12 -0
- data/lib/bundles/inspec-habitat/cli.rb +32 -0
- data/lib/bundles/inspec-habitat/log.rb +10 -0
- data/lib/bundles/inspec-habitat/profile.rb +334 -0
- data/lib/inspec/metadata.rb +22 -4
- data/lib/inspec/profile.rb +10 -4
- data/lib/inspec/resource.rb +1 -0
- data/lib/inspec/rspec_json_formatter.rb +66 -23
- data/lib/inspec/shell.rb +15 -8
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/crontab.rb +83 -0
- data/lib/resources/package.rb +1 -1
- data/lib/resources/packages.rb +42 -18
- data/lib/source_readers/inspec.rb +1 -1
- metadata +24 -6
- data/examples/meta-profile/vendor/793adcbb91cfc2da0044bb9cbf0863773ae2cf89ce9b8343b4295b137f70897b.tar.gz +0 -0
- data/examples/profile/inspec.lock +0 -3
- data/lib/resources/.ssh_conf.rb.swp +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6d6a9596b5e8f107982bbf0772679d92bd903b24
         | 
| 4 | 
            +
              data.tar.gz: 87f6fd87e179535f8d78512cc6a35798076cb51f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 73b6a1520ce732b59972ab69a1de54febd1512d06cf3102d81ad678ad7bedf85755a214d1ddc803fe7deda9cf2b1f26c0abc49716281f2690f3c4514dea15f3f
         | 
| 7 | 
            +
              data.tar.gz: 1333f99c255ff0853315186c023fa22d419a3a9ec1e770bc2e6983eed371a15d5f91f3d65ff889deead3cef15d7eec0641fb4b33dc72abaaa1f2c3346de0a173
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,7 +1,43 @@ | |
| 1 1 | 
             
            # Change Log
         | 
| 2 2 |  | 
| 3 | 
            -
            ## [ | 
| 4 | 
            -
            [Full Changelog](https://github.com/chef/inspec/compare/v1.14. | 
| 3 | 
            +
            ## [v1.15.0](https://github.com/chef/inspec/tree/v1.15.0) (2017-02-27)
         | 
| 4 | 
            +
            [Full Changelog](https://github.com/chef/inspec/compare/v1.14.1...v1.15.0)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            **Implemented enhancements:**
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            - Wrong rendering of InSpec.io header [\#1421](https://github.com/chef/inspec/issues/1421)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            **Fixed bugs:**
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            - New Inspec.io is crashing on Edge if window is resized to a smaller window [\#1420](https://github.com/chef/inspec/issues/1420)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            **Closed issues:**
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            - Colours and symbols broken on Windows [\#1508](https://github.com/chef/inspec/issues/1508)
         | 
| 17 | 
            +
            - be\_reacheable matcher for host resource should not always use ping on linux [\#1504](https://github.com/chef/inspec/issues/1504)
         | 
| 18 | 
            +
            - Inspec login fails [\#1503](https://github.com/chef/inspec/issues/1503)
         | 
| 19 | 
            +
            - Develop an inspec test for selinux [\#1496](https://github.com/chef/inspec/issues/1496)
         | 
| 20 | 
            +
            - Inspec.io: Add webinar/notifications bar in index.html [\#1495](https://github.com/chef/inspec/issues/1495)
         | 
| 21 | 
            +
            - Inspec.io: Try Demo Button Bug [\#1494](https://github.com/chef/inspec/issues/1494)
         | 
| 22 | 
            +
            - \[chef-compliance\]  Scan Report Calculations [\#1491](https://github.com/chef/inspec/issues/1491)
         | 
| 23 | 
            +
            - Create url for demo that can be pointed to from outbound campaigns [\#1485](https://github.com/chef/inspec/issues/1485)
         | 
| 24 | 
            +
            - After inspec update from 1.5 to 1.10 it breaks with \[undefined method `\[\]=' for nil:NilClass\] [\#1456](https://github.com/chef/inspec/issues/1456)
         | 
| 25 | 
            +
            - Inspec.io and IE11 [\#1437](https://github.com/chef/inspec/issues/1437)
         | 
| 26 | 
            +
            - Link to robert\_config.rb is broken on inspec.io [\#1226](https://github.com/chef/inspec/issues/1226)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            **Merged pull requests:**
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            - Fix formatting and colors on Windows [\#1510](https://github.com/chef/inspec/pull/1510) ([trickyearlobe](https://github.com/trickyearlobe))
         | 
| 31 | 
            +
            - Adding a Habitat profile artifact creator [\#1505](https://github.com/chef/inspec/pull/1505) ([adamleff](https://github.com/adamleff))
         | 
| 32 | 
            +
            - create inspec.io/tutorial.html [\#1490](https://github.com/chef/inspec/pull/1490) ([arlimus](https://github.com/arlimus))
         | 
| 33 | 
            +
            - Doc fix for SourceReaders::InspecReader [\#1489](https://github.com/chef/inspec/pull/1489) ([adamleff](https://github.com/adamleff))
         | 
| 34 | 
            +
            - Generate default profile names, fix bug when using multiple flat profiles [\#1488](https://github.com/chef/inspec/pull/1488) ([adamleff](https://github.com/adamleff))
         | 
| 35 | 
            +
            - Packages resource support for RedHat [\#1487](https://github.com/chef/inspec/pull/1487) ([alexpop](https://github.com/alexpop))
         | 
| 36 | 
            +
            - Adding new crontab resource [\#1482](https://github.com/chef/inspec/pull/1482) ([adamleff](https://github.com/adamleff))
         | 
| 37 | 
            +
            - Provide target info on shell invocation [\#1475](https://github.com/chef/inspec/pull/1475) ([adamleff](https://github.com/adamleff))
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            ## [v1.14.1](https://github.com/chef/inspec/tree/v1.14.1) (2017-02-10)
         | 
| 40 | 
            +
            [Full Changelog](https://github.com/chef/inspec/compare/v1.14.0...v1.14.1)
         | 
| 5 41 |  | 
| 6 42 | 
             
            **Closed issues:**
         | 
| 7 43 |  | 
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            title: About the crontab Resource
         | 
| 3 | 
            +
            ---
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # crontab
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Use the `crontab` InSpec audit resource to test the crontab entries for a particular user on the system.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## Syntax
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            A `crontab` resource block declares a user (which defaults to the current user, if not specified), and then the details to be tested, such as the schedule elements for each crontab entry or the commands itself:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                describe crontab do
         | 
| 14 | 
            +
                  its('commands') { should include '/some/scheduled/task.sh' }
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ## Matchers
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            This InSpec audit resource has the following matchers:
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### be
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            <%= partial "/shared/matcher_be" %>
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ### cmp
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            <%= partial "/shared/matcher_cmp" %>
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            ### eq
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            <%= partial "/shared/matcher_eq" %>
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ### include
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            <%= partial "/shared/matcher_include" %>
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            ### match
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            <%= partial "/shared/matcher_match" %>
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            ## Examples
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            The following examples show how to use this InSpec audit resource.
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            ### Test that root's crontab has a particular command
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                describe crontab('root') do
         | 
| 48 | 
            +
                  its('commands') { should include '/path/to/some/script' }
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ### Test that myuser's crontab entry for command '/home/myuser/build.sh' runs every minute
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                describe crontab('myuser').commands('/home/myuser/build.sh') do
         | 
| 54 | 
            +
                  its('hours') { should cmp '*' }
         | 
| 55 | 
            +
                  its('minutes') { should cmp '*' }
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            ### Test that the logged-in user's crontab has no tasks set to run on every hour and every minute
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                describe crontab.where({'hour' => '*', 'minute' => '*'}) do
         | 
| 61 | 
            +
                  its('entries.length') { should cmp '0' }
         | 
| 62 | 
            +
                end
         | 
| @@ -3,9 +3,9 @@ lockfile_version: 1 | |
| 3 3 | 
             
            depends:
         | 
| 4 4 | 
             
            - name: profile
         | 
| 5 5 | 
             
              resolved_source:
         | 
| 6 | 
            -
                path: "/ | 
| 6 | 
            +
                path: "/Users/aleff/projects/inspec/examples/profile"
         | 
| 7 7 | 
             
              version_constraints: ">= 0"
         | 
| 8 8 | 
             
            - name: profile-attribute
         | 
| 9 9 | 
             
              resolved_source:
         | 
| 10 | 
            -
                path: "/ | 
| 10 | 
            +
                path: "/Users/aleff/projects/inspec/examples/profile-attribute"
         | 
| 11 11 | 
             
              version_constraints: ">= 0"
         | 
| @@ -9,7 +9,7 @@ depends: | |
| 9 9 | 
             
            - name: ssl-benchmark
         | 
| 10 10 | 
             
              resolved_source:
         | 
| 11 11 | 
             
                url: https://github.com/dev-sec/ssl-benchmark/archive/master.tar.gz
         | 
| 12 | 
            -
                sha256:  | 
| 12 | 
            +
                sha256: 9ad48391d4e6efff0a13d06736c5b075fb021410e0a629e087bc21e9617d957c
         | 
| 13 13 | 
             
              version_constraints: ">= 0"
         | 
| 14 14 | 
             
            - name: windows-patch-benchmark
         | 
| 15 15 | 
             
              resolved_source:
         | 
| Binary file | 
    
        data/inspec.gemspec
    CHANGED
    
    
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # author: Adam Leff
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            libdir = File.dirname(__FILE__)
         | 
| 5 | 
            +
            $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Habitat
         | 
| 8 | 
            +
              autoload :Log,     'inspec-habitat/log'
         | 
| 9 | 
            +
              autoload :Profile, 'inspec-habitat/profile'
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            require 'inspec-habitat/cli'
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # author: Adam Leff
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'thor'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Habitat
         | 
| 7 | 
            +
              class HabitatProfileCLI < Thor
         | 
| 8 | 
            +
                namespace 'habitat profile'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                desc 'create PATH', 'Create a Habitat artifact for the profile found at PATH'
         | 
| 11 | 
            +
                option :output_dir, type: :string, required: false,
         | 
| 12 | 
            +
                  desc: 'Directory in which to save the generated Habitat artifact. Default: current directory'
         | 
| 13 | 
            +
                def create(path)
         | 
| 14 | 
            +
                  puts options
         | 
| 15 | 
            +
                  Habitat::Profile.create(path, options)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                desc 'upload PATH', 'Create a Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
         | 
| 19 | 
            +
                def upload(path)
         | 
| 20 | 
            +
                  Habitat::Profile.upload(path, options)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              class HabitatCLI < Inspec::BaseCLI
         | 
| 25 | 
            +
                namespace 'habitat'
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                desc 'profile', 'Manage InSpec profiles as Habitat artifacts'
         | 
| 28 | 
            +
                subcommand 'profile', HabitatProfileCLI
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              Inspec::Plugins::CLI.add_subcommand(HabitatCLI, 'habitat', 'habitat SUBCOMMAND ...', 'Commands for InSpec + Habitat Integration', {})
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,334 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # author: Adam Leff
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'mixlib/shellout'
         | 
| 5 | 
            +
            require 'toml'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Habitat
         | 
| 8 | 
            +
              class Profile # rubocop:disable Metrics/ClassLength
         | 
| 9 | 
            +
                attr_reader :options, :path, :profile
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def self.create(path, options = {})
         | 
| 12 | 
            +
                  creator = new(path, options)
         | 
| 13 | 
            +
                  hart_file = creator.create
         | 
| 14 | 
            +
                  creator.copy(hart_file)
         | 
| 15 | 
            +
                ensure
         | 
| 16 | 
            +
                  creator.delete_work_dir
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def self.upload(path, options = {})
         | 
| 20 | 
            +
                  uploader = new(path, options)
         | 
| 21 | 
            +
                  uploader.upload
         | 
| 22 | 
            +
                ensure
         | 
| 23 | 
            +
                  uploader.delete_work_dir
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def initialize(path, options = {})
         | 
| 27 | 
            +
                  @path    = path
         | 
| 28 | 
            +
                  @options = options
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  log_level = options.fetch('log_level', 'info')
         | 
| 31 | 
            +
                  Habitat::Log.level(log_level.to_sym)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def create
         | 
| 35 | 
            +
                  Habitat::Log.info("Creating a Habitat artifact for profile: #{path}")
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  validate_habitat_installed
         | 
| 38 | 
            +
                  validate_habitat_origin
         | 
| 39 | 
            +
                  create_profile_object
         | 
| 40 | 
            +
                  copy_profile_to_work_dir
         | 
| 41 | 
            +
                  create_plan
         | 
| 42 | 
            +
                  create_run_hook
         | 
| 43 | 
            +
                  create_default_config
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  # returns the path to the .hart file in the work directory
         | 
| 46 | 
            +
                  build_hart
         | 
| 47 | 
            +
                rescue => e
         | 
| 48 | 
            +
                  Habitat::Log.debug(e.backtrace.join("\n"))
         | 
| 49 | 
            +
                  exit_with_error(
         | 
| 50 | 
            +
                    'Unable to generate Habitat artifact.',
         | 
| 51 | 
            +
                    "#{e.class} -- #{e.message}",
         | 
| 52 | 
            +
                  )
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def copy(hart_file)
         | 
| 56 | 
            +
                  validate_output_dir
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  Habitat::Log.info("Copying artifact to #{output_dir}...")
         | 
| 59 | 
            +
                  copy_hart(hart_file)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def upload
         | 
| 63 | 
            +
                  validate_habitat_auth_token
         | 
| 64 | 
            +
                  hart_file = create
         | 
| 65 | 
            +
                  upload_hart(hart_file)
         | 
| 66 | 
            +
                rescue => e
         | 
| 67 | 
            +
                  Habitat::Log.debug(e.backtrace.join("\n"))
         | 
| 68 | 
            +
                  exit_with_error(
         | 
| 69 | 
            +
                    'Unable to upload Habitat artifact.',
         | 
| 70 | 
            +
                    "#{e.class} -- #{e.message}",
         | 
| 71 | 
            +
                  )
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def delete_work_dir
         | 
| 75 | 
            +
                  Habitat::Log.debug("Deleting work directory #{work_dir}")
         | 
| 76 | 
            +
                  FileUtils.rm_rf(work_dir) if Dir.exist?(work_dir)
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                private
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def create_profile_object
         | 
| 82 | 
            +
                  @profile = Inspec::Profile.for_target(path, {})
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def verify_profile
         | 
| 86 | 
            +
                  Habitat::Log.info('Checking to see if the profile is valid...')
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  unless profile.check[:summary][:valid]
         | 
| 89 | 
            +
                    exit_with_error('Profile check failed. Please fix the profile before creating a Habitat artifact.')
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  Habitat::Log.info('Profile is valid.')
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def validate_habitat_installed
         | 
| 96 | 
            +
                  Habitat::Log.info('Checking to see if Habitat is installed...')
         | 
| 97 | 
            +
                  cmd = Mixlib::ShellOut.new('hab --version')
         | 
| 98 | 
            +
                  cmd.run_command
         | 
| 99 | 
            +
                  if cmd.error?
         | 
| 100 | 
            +
                    exit_with_error('Unable to run Habitat commands.', cmd.stderr)
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def validate_habitat_origin
         | 
| 105 | 
            +
                  if habitat_origin.nil?
         | 
| 106 | 
            +
                    exit_with_error(
         | 
| 107 | 
            +
                      'Unable to determine Habitat origin name.',
         | 
| 108 | 
            +
                      'Run `hab setup` or set the HAB_ORIGIN environment variable.',
         | 
| 109 | 
            +
                    )
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def validate_habitat_auth_token
         | 
| 114 | 
            +
                  if habitat_auth_token.nil?
         | 
| 115 | 
            +
                    exit_with_error(
         | 
| 116 | 
            +
                      'Unable to determine Habitat auth token for publishing.',
         | 
| 117 | 
            +
                      'Run `hab setup` or set the HAB_AUTH_TOKEN environment variable.',
         | 
| 118 | 
            +
                    )
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def validate_output_dir
         | 
| 123 | 
            +
                  exit_with_error("Output directory #{output_dir} is not a directory or does not exist.") unless
         | 
| 124 | 
            +
                    File.directory?(output_dir)
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                def work_dir
         | 
| 128 | 
            +
                  return @work_dir if @work_dir
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  @work_dir ||= Dir.mktmpdir('inspec-habitat-exporter')
         | 
| 131 | 
            +
                  Dir.mkdir(File.join(@work_dir, 'src'))
         | 
| 132 | 
            +
                  Dir.mkdir(File.join(@work_dir, 'habitat'))
         | 
| 133 | 
            +
                  Dir.mkdir(File.join(@work_dir, 'habitat', 'hooks'))
         | 
| 134 | 
            +
                  Habitat::Log.debug("Generated work directory #{@work_dir}")
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  @work_dir
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                def copy_profile_to_work_dir
         | 
| 140 | 
            +
                  Habitat::Log.info('Copying profile contents to the work directory...')
         | 
| 141 | 
            +
                  profile.files.each do |f|
         | 
| 142 | 
            +
                    src = File.join(profile.root_path, f)
         | 
| 143 | 
            +
                    dst = File.join(work_dir, 'src', f)
         | 
| 144 | 
            +
                    if File.directory?(f)
         | 
| 145 | 
            +
                      Habitat::Log.debug("Creating directory #{dst}")
         | 
| 146 | 
            +
                      FileUtils.mkdir_p(dst)
         | 
| 147 | 
            +
                    else
         | 
| 148 | 
            +
                      Habitat::Log.debug("Copying file #{src} to #{dst}")
         | 
| 149 | 
            +
                      FileUtils.cp_r(src, dst)
         | 
| 150 | 
            +
                    end
         | 
| 151 | 
            +
                  end
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def create_plan
         | 
| 155 | 
            +
                  plan_file = File.join(work_dir, 'habitat', 'plan.sh')
         | 
| 156 | 
            +
                  Habitat::Log.info("Generating Habitat plan at #{plan_file}...")
         | 
| 157 | 
            +
                  File.write(plan_file, plan_contents)
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def create_run_hook
         | 
| 161 | 
            +
                  run_hook_file = File.join(work_dir, 'habitat', 'hooks', 'run')
         | 
| 162 | 
            +
                  Habitat::Log.info("Generating a Habitat run hook at #{run_hook_file}...")
         | 
| 163 | 
            +
                  File.write(run_hook_file, run_hook_contents)
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                def create_default_config
         | 
| 167 | 
            +
                  default_toml = File.join(work_dir, 'habitat', 'default.toml')
         | 
| 168 | 
            +
                  Habitat::Log.info("Generating Habitat's default.toml configuration...")
         | 
| 169 | 
            +
                  File.write(default_toml, 'sleep_time = 300')
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                def build_hart
         | 
| 173 | 
            +
                  Habitat::Log.info('Building our Habitat artifact...')
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  env = {
         | 
| 176 | 
            +
                    'TERM'               => 'vt100',
         | 
| 177 | 
            +
                    'HAB_ORIGIN'         => habitat_origin,
         | 
| 178 | 
            +
                    'HAB_NONINTERACTIVE' => 'true',
         | 
| 179 | 
            +
                  }
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  env['RUST_LOG'] = 'debug' if Habitat::Log.level == :debug
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                  # TODO: Would love to use Mixlib::ShellOut here, but it doesn't
         | 
| 184 | 
            +
                  # seem to preserve the STDIN tty, and docker gets angry.
         | 
| 185 | 
            +
                  Dir.chdir(work_dir) do
         | 
| 186 | 
            +
                    unless system(env, 'hab studio build .')
         | 
| 187 | 
            +
                      exit_with_error('Unable to build the Habitat artifact.')
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  hart_files = Dir.glob(File.join(work_dir, 'results', '*.hart'))
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  if hart_files.length > 1
         | 
| 194 | 
            +
                    exit_with_error('More than one Habitat artifact was created which was not expected.')
         | 
| 195 | 
            +
                  elsif hart_files.empty?
         | 
| 196 | 
            +
                    exit_with_error('No Habitat artifact was created.')
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                  hart_files.first
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                def copy_hart(working_dir_hart)
         | 
| 203 | 
            +
                  hart_basename = File.basename(working_dir_hart)
         | 
| 204 | 
            +
                  dst = File.join(output_dir, hart_basename)
         | 
| 205 | 
            +
                  FileUtils.cp(working_dir_hart, dst)
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                  dst
         | 
| 208 | 
            +
                end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                def upload_hart(hart_file)
         | 
| 211 | 
            +
                  Habitat::Log.info('Uploading the Habitat artifact to our Depot...')
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                  env = {
         | 
| 214 | 
            +
                    'TERM'               => 'vt100',
         | 
| 215 | 
            +
                    'HAB_AUTH_TOKEN'     => habitat_auth_token,
         | 
| 216 | 
            +
                    'HAB_NONINTERACTIVE' => 'true',
         | 
| 217 | 
            +
                  }
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                  env['HAB_DEPOT_URL'] = ENV['HAB_DEPOT_URL'] if ENV['HAB_DEPOT_URL']
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                  cmd = Mixlib::ShellOut.new("hab pkg upload #{hart_file}", env: env)
         | 
| 222 | 
            +
                  cmd.run_command
         | 
| 223 | 
            +
                  if cmd.error?
         | 
| 224 | 
            +
                    exit_with_error(
         | 
| 225 | 
            +
                      'Unable to upload Habitat artifact to the Depot.',
         | 
| 226 | 
            +
                      cmd.stdout,
         | 
| 227 | 
            +
                      cmd.stderr,
         | 
| 228 | 
            +
                    )
         | 
| 229 | 
            +
                  end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                  Habitat::Log.info('Upload complete!')
         | 
| 232 | 
            +
                end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                def habitat_origin
         | 
| 235 | 
            +
                  ENV['HAB_ORIGIN'] || habitat_cli_config['origin']
         | 
| 236 | 
            +
                end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                def habitat_auth_token
         | 
| 239 | 
            +
                  ENV['HAB_AUTH_TOKEN'] || habitat_cli_config['auth_token']
         | 
| 240 | 
            +
                end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                def habitat_cli_config
         | 
| 243 | 
            +
                  return @cli_config if @cli_config
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                  config_file = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
         | 
| 246 | 
            +
                  return {} unless File.exist?(config_file)
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                  @cli_config = TOML.load_file(config_file)
         | 
| 249 | 
            +
                end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                def output_dir
         | 
| 252 | 
            +
                  options[:output_dir] || Dir.pwd
         | 
| 253 | 
            +
                end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                def exit_with_error(*errors)
         | 
| 256 | 
            +
                  errors.each do |error_msg|
         | 
| 257 | 
            +
                    Habitat::Log.error(error_msg)
         | 
| 258 | 
            +
                  end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                  exit 1
         | 
| 261 | 
            +
                end
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                def package_name
         | 
| 264 | 
            +
                  "inspec-profile-#{profile.name}"
         | 
| 265 | 
            +
                end
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                def plan_contents
         | 
| 268 | 
            +
                  plan = <<-EOL
         | 
| 269 | 
            +
            pkg_name=#{package_name}
         | 
| 270 | 
            +
            pkg_version=#{profile.version}
         | 
| 271 | 
            +
            pkg_origin=#{habitat_origin}
         | 
| 272 | 
            +
            pkg_source="nosuchfile.tar.gz"
         | 
| 273 | 
            +
            pkg_deps=(chef/inspec)
         | 
| 274 | 
            +
            pkg_build_deps=()
         | 
| 275 | 
            +
            EOL
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                  plan += "pkg_license='#{profile.metadata.params[:license]}'\n\n" if profile.metadata.params[:license]
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                  plan += <<-EOL
         | 
| 280 | 
            +
            do_download() {
         | 
| 281 | 
            +
              return 0
         | 
| 282 | 
            +
            }
         | 
| 283 | 
            +
             | 
| 284 | 
            +
            do_verify() {
         | 
| 285 | 
            +
              return 0
         | 
| 286 | 
            +
            }
         | 
| 287 | 
            +
             | 
| 288 | 
            +
            do_unpack() {
         | 
| 289 | 
            +
              return 0
         | 
| 290 | 
            +
            }
         | 
| 291 | 
            +
             | 
| 292 | 
            +
            do_build() {
         | 
| 293 | 
            +
              cp -vr $PLAN_CONTEXT/../src/* $HAB_CACHE_SRC_PATH/$pkg_dirname
         | 
| 294 | 
            +
            }
         | 
| 295 | 
            +
             | 
| 296 | 
            +
            do_install() {
         | 
| 297 | 
            +
              cp -R . ${pkg_prefix}/dist
         | 
| 298 | 
            +
            }
         | 
| 299 | 
            +
                  EOL
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                  plan
         | 
| 302 | 
            +
                end
         | 
| 303 | 
            +
             | 
| 304 | 
            +
                def run_hook_contents
         | 
| 305 | 
            +
                  <<-EOL
         | 
| 306 | 
            +
            #!/bin/sh
         | 
| 307 | 
            +
             | 
| 308 | 
            +
            export PATH=${PATH}:$(hab pkg path core/ruby)/bin
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            PROFILE_IDENT="#{habitat_origin}/#{package_name}"
         | 
| 311 | 
            +
            SLEEP_TIME={{cfg.sleep_time}}
         | 
| 312 | 
            +
             | 
| 313 | 
            +
            # InSpec will try to create a .inspec directory, so this needs to be somewhere writable by the hab user
         | 
| 314 | 
            +
            cd {{pkg.svc_var_path}}
         | 
| 315 | 
            +
             | 
| 316 | 
            +
            while true; do
         | 
| 317 | 
            +
              echo "Executing InSpec for ${PROFILE_IDENT}"
         | 
| 318 | 
            +
              hab pkg exec chef/inspec inspec exec $(hab pkg path ${PROFILE_IDENT})/dist --format=cli 2>&1
         | 
| 319 | 
            +
              RC=$?
         | 
| 320 | 
            +
             | 
| 321 | 
            +
              echo ""
         | 
| 322 | 
            +
              if [ "x${RC}" == "x0" ]; then
         | 
| 323 | 
            +
                echo "InSpec run completed successfully."
         | 
| 324 | 
            +
              else
         | 
| 325 | 
            +
                echo "InSpec run did NOT complete successfully."
         | 
| 326 | 
            +
              fi
         | 
| 327 | 
            +
             | 
| 328 | 
            +
              echo "sleeping for ${SLEEP_TIME} seconds"
         | 
| 329 | 
            +
              sleep ${SLEEP_TIME}
         | 
| 330 | 
            +
            done
         | 
| 331 | 
            +
                  EOL
         | 
| 332 | 
            +
                end
         | 
| 333 | 
            +
              end
         | 
| 334 | 
            +
            end
         | 
    
        data/lib/inspec/metadata.rb
    CHANGED
    
    | @@ -177,13 +177,31 @@ module Inspec | |
| 177 177 | 
             
                  end
         | 
| 178 178 | 
             
                end
         | 
| 179 179 |  | 
| 180 | 
            -
                def self. | 
| 180 | 
            +
                def self.finalize_name(metadata, profile_id, original_target)
         | 
| 181 | 
            +
                  # profile_id always overwrites whatever already exists as the name
         | 
| 182 | 
            +
                  unless profile_id.to_s.empty?
         | 
| 183 | 
            +
                    metadata.params[:name] = profile_id.to_s
         | 
| 184 | 
            +
                    return
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                  # don't overwrite an existing name
         | 
| 188 | 
            +
                  return unless metadata.params[:name].nil?
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                  # if there's a title, there is no need to set a name too
         | 
| 191 | 
            +
                  return unless metadata.params[:title].nil?
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  # create a new name based on the original target if it exists
         | 
| 194 | 
            +
                  metadata.params[:name] = "tests from #{original_target}" unless original_target.to_s.empty?
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                def self.finalize(metadata, profile_id, options, logger = nil)
         | 
| 181 198 | 
             
                  return nil if metadata.nil?
         | 
| 182 199 | 
             
                  param = metadata.params || {}
         | 
| 183 | 
            -
                   | 
| 200 | 
            +
                  options ||= {}
         | 
| 184 201 | 
             
                  param['version'] = param['version'].to_s unless param['version'].nil?
         | 
| 185 202 | 
             
                  metadata.params = symbolize_keys(param)
         | 
| 186 203 | 
             
                  metadata.params[:supports] = finalize_supports(metadata.params[:supports], logger)
         | 
| 204 | 
            +
                  finalize_name(metadata, profile_id, options[:target])
         | 
| 187 205 |  | 
| 188 206 | 
             
                  metadata
         | 
| 189 207 | 
             
                end
         | 
| @@ -191,13 +209,13 @@ module Inspec | |
| 191 209 | 
             
                def self.from_yaml(ref, contents, profile_id, logger = nil)
         | 
| 192 210 | 
             
                  res = Metadata.new(ref, logger)
         | 
| 193 211 | 
             
                  res.params = YAML.load(contents)
         | 
| 194 | 
            -
                  finalize(res, profile_id, logger)
         | 
| 212 | 
            +
                  finalize(res, profile_id, {}, logger)
         | 
| 195 213 | 
             
                end
         | 
| 196 214 |  | 
| 197 215 | 
             
                def self.from_ruby(ref, contents, profile_id, logger = nil)
         | 
| 198 216 | 
             
                  res = Metadata.new(ref, logger)
         | 
| 199 217 | 
             
                  res.instance_eval(contents, ref, 1)
         | 
| 200 | 
            -
                  finalize(res, profile_id, logger)
         | 
| 218 | 
            +
                  finalize(res, profile_id, {}, logger)
         | 
| 201 219 | 
             
                end
         | 
| 202 220 |  | 
| 203 221 | 
             
                def self.from_ref(ref, contents, profile_id, logger = nil)
         | 
    
        data/lib/inspec/profile.rb
    CHANGED
    
    | @@ -82,7 +82,7 @@ module Inspec | |
| 82 82 |  | 
| 83 83 | 
             
                # rubocop:disable Metrics/AbcSize
         | 
| 84 84 | 
             
                def initialize(source_reader, options = {})
         | 
| 85 | 
            -
                  @target = options | 
| 85 | 
            +
                  @target = options[:target]
         | 
| 86 86 | 
             
                  @logger = options[:logger] || Logger.new(nil)
         | 
| 87 87 | 
             
                  @locked_dependencies = options[:dependencies]
         | 
| 88 88 | 
             
                  @controls = options[:controls] || []
         | 
| @@ -94,7 +94,7 @@ module Inspec | |
| 94 94 | 
             
                  @source_reader = source_reader
         | 
| 95 95 | 
             
                  @tests_collected = false
         | 
| 96 96 | 
             
                  @libraries_loaded = false
         | 
| 97 | 
            -
                  Metadata.finalize(@source_reader.metadata, @profile_id)
         | 
| 97 | 
            +
                  Metadata.finalize(@source_reader.metadata, @profile_id, options)
         | 
| 98 98 | 
             
                  @runner_context =
         | 
| 99 99 | 
             
                    options[:profile_context] ||
         | 
| 100 100 | 
             
                    Inspec::ProfileContext.for_profile(self, @backend, @attr_values)
         | 
| @@ -318,8 +318,6 @@ module Inspec | |
| 318 318 |  | 
| 319 319 | 
             
                  # display all files that will be part of the archive
         | 
| 320 320 | 
             
                  @logger.debug 'Add the following files to archive:'
         | 
| 321 | 
            -
                  root_path = @source_reader.target.prefix
         | 
| 322 | 
            -
                  files = @source_reader.target.files
         | 
| 323 321 | 
             
                  files.each { |f| @logger.debug '    ' + f }
         | 
| 324 322 |  | 
| 325 323 | 
             
                  if opts[:zip]
         | 
| @@ -350,6 +348,14 @@ module Inspec | |
| 350 348 | 
             
                  File.join(cwd, 'inspec.lock')
         | 
| 351 349 | 
             
                end
         | 
| 352 350 |  | 
| 351 | 
            +
                def root_path
         | 
| 352 | 
            +
                  @source_reader.target.prefix
         | 
| 353 | 
            +
                end
         | 
| 354 | 
            +
             | 
| 355 | 
            +
                def files
         | 
| 356 | 
            +
                  @source_reader.target.files
         | 
| 357 | 
            +
                end
         | 
| 358 | 
            +
             | 
| 353 359 | 
             
                #
         | 
| 354 360 | 
             
                # TODO(ssd): Relative path handling really needs to be carefully
         | 
| 355 361 | 
             
                # thought through, especially with respect to relative paths in
         | 
    
        data/lib/inspec/resource.rb
    CHANGED
    
    
| @@ -217,7 +217,17 @@ class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLengt | |
| 217 217 | 
             
              end
         | 
| 218 218 |  | 
| 219 219 | 
             
              def profile_contains_example?(profile, example)
         | 
| 220 | 
            -
                profile[:name] | 
| 220 | 
            +
                profile_name = profile[:name]
         | 
| 221 | 
            +
                example_profile_id = example[:profile_id]
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                # if either the profile name is nil or the profile in the given example
         | 
| 224 | 
            +
                # is nil, assume the profile doesn't contain the example and default
         | 
| 225 | 
            +
                # to creating a new profile. Otherwise, for profiles that have no
         | 
| 226 | 
            +
                # metadata, this may incorrectly match a profile that does not contain
         | 
| 227 | 
            +
                # this example, leading to Ruby exceptions.
         | 
| 228 | 
            +
                return false if profile_name.nil? || example_profile_id.nil?
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                profile_name == example_profile_id
         | 
| 221 231 | 
             
              end
         | 
| 222 232 |  | 
| 223 233 | 
             
              def move_example_into_control(example, control)
         | 
| @@ -238,27 +248,60 @@ end | |
| 238 248 | 
             
            class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
         | 
| 239 249 | 
             
              RSpec::Core::Formatters.register self, :close
         | 
| 240 250 |  | 
| 241 | 
            -
               | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
                 | 
| 245 | 
            -
                 | 
| 246 | 
            -
                 | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
             | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 254 | 
            -
                 | 
| 255 | 
            -
             | 
| 256 | 
            -
                 | 
| 257 | 
            -
                 | 
| 258 | 
            -
                 | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
               | 
| 251 | 
            +
              case RUBY_PLATFORM
         | 
| 252 | 
            +
              when /windows|mswin|msys|mingw|cygwin/
         | 
| 253 | 
            +
             | 
| 254 | 
            +
                # Most currently available Windows terminals have poor support
         | 
| 255 | 
            +
                # for ANSI extended colors
         | 
| 256 | 
            +
                COLORS = {
         | 
| 257 | 
            +
                  'critical' => "\033[0;1;31m",
         | 
| 258 | 
            +
                  'major'    => "\033[0;1;31m",
         | 
| 259 | 
            +
                  'minor'    => "\033[0;36m",
         | 
| 260 | 
            +
                  'failed'   => "\033[0;1;31m",
         | 
| 261 | 
            +
                  'passed'   => "\033[0;1;32m",
         | 
| 262 | 
            +
                  'skipped'  => "\033[0;37m",
         | 
| 263 | 
            +
                  'reset'    => "\033[0m",
         | 
| 264 | 
            +
                }.freeze
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                # Most currently available Windows terminals have poor support
         | 
| 267 | 
            +
                # for UTF-8 characters so use these boring indicators
         | 
| 268 | 
            +
                INDICATORS = {
         | 
| 269 | 
            +
                  'critical' => '  [CRIT]  ',
         | 
| 270 | 
            +
                  'major'    => '  [MAJR]  ',
         | 
| 271 | 
            +
                  'minor'    => '  [MINR]  ',
         | 
| 272 | 
            +
                  'failed'   => '  [FAIL]  ',
         | 
| 273 | 
            +
                  'skipped'  => '  [SKIP]  ',
         | 
| 274 | 
            +
                  'passed'   => '  [PASS]  ',
         | 
| 275 | 
            +
                  'unknown'  => '  [UNKN]  ',
         | 
| 276 | 
            +
                  'empty'    => '     ',
         | 
| 277 | 
            +
                  'small'    => '   ',
         | 
| 278 | 
            +
                }.freeze
         | 
| 279 | 
            +
              else
         | 
| 280 | 
            +
                # Extended colors for everyone else
         | 
| 281 | 
            +
                COLORS = {
         | 
| 282 | 
            +
                  'critical' => "\033[38;5;9m",
         | 
| 283 | 
            +
                  'major'    => "\033[38;5;208m",
         | 
| 284 | 
            +
                  'minor'    => "\033[0;36m",
         | 
| 285 | 
            +
                  'failed'   => "\033[38;5;9m",
         | 
| 286 | 
            +
                  'passed'   => "\033[38;5;41m",
         | 
| 287 | 
            +
                  'skipped'  => "\033[38;5;247m",
         | 
| 288 | 
            +
                  'reset'    => "\033[0m",
         | 
| 289 | 
            +
                }.freeze
         | 
| 290 | 
            +
             | 
| 291 | 
            +
                # Groovy UTF-8 characters for everyone else...
         | 
| 292 | 
            +
                # ...even though they probably only work on Mac
         | 
| 293 | 
            +
                INDICATORS = {
         | 
| 294 | 
            +
                  'critical' => '  ×  ',
         | 
| 295 | 
            +
                  'major'    => '  ∅  ',
         | 
| 296 | 
            +
                  'minor'    => '  ⊚  ',
         | 
| 297 | 
            +
                  'failed'   => '  ×  ',
         | 
| 298 | 
            +
                  'skipped'  => '  ↺  ',
         | 
| 299 | 
            +
                  'passed'   => '  ✔  ',
         | 
| 300 | 
            +
                  'unknown'  => '  ?  ',
         | 
| 301 | 
            +
                  'empty'    => '     ',
         | 
| 302 | 
            +
                  'small'    => '   ',
         | 
| 303 | 
            +
                }.freeze
         | 
| 304 | 
            +
              end
         | 
| 262 305 |  | 
| 263 306 | 
             
              MULTI_TEST_CONTROL_SUMMARY_MAX_LEN = 60
         | 
| 264 307 |  | 
| @@ -458,7 +501,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength | |
| 458 501 | 
             
                  output.puts "Profile: #{profile[:title]} (#{profile[:name] || 'unknown'})"
         | 
| 459 502 | 
             
                end
         | 
| 460 503 |  | 
| 461 | 
            -
                output.puts 'Version: ' + (profile[:version] || ' | 
| 504 | 
            +
                output.puts 'Version: ' + (profile[:version] || '(not specified)')
         | 
| 462 505 | 
             
                print_target
         | 
| 463 506 | 
             
                profile[:already_printed] = true
         | 
| 464 507 | 
             
              end
         | 
    
        data/lib/inspec/shell.rb
    CHANGED
    
    | @@ -42,6 +42,8 @@ module Inspec | |
| 42 42 | 
             
                  # Add a help menu as the default intro
         | 
| 43 43 | 
             
                  Pry.hooks.add_hook(:before_session, 'inspec_intro') do
         | 
| 44 44 | 
             
                    intro
         | 
| 45 | 
            +
                    print_target_info
         | 
| 46 | 
            +
                    puts
         | 
| 45 47 | 
             
                  end
         | 
| 46 48 |  | 
| 47 49 | 
             
                  # Track the rules currently registered and what their merge count is.
         | 
| @@ -94,10 +96,20 @@ module Inspec | |
| 94 96 | 
             
                  puts
         | 
| 95 97 | 
             
                end
         | 
| 96 98 |  | 
| 99 | 
            +
                def print_target_info
         | 
| 100 | 
            +
                  ctx = @runner.backend
         | 
| 101 | 
            +
                  puts <<EOF
         | 
| 102 | 
            +
            You are currently running on:
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                OS platform:  #{mark ctx.os[:name] || 'unknown'}
         | 
| 105 | 
            +
                OS family:  #{mark ctx.os[:family] || 'unknown'}
         | 
| 106 | 
            +
                OS release: #{mark ctx.os[:release] || 'unknown'}
         | 
| 107 | 
            +
            EOF
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 97 110 | 
             
                def help(resource = nil)
         | 
| 98 111 | 
             
                  if resource.nil?
         | 
| 99 112 |  | 
| 100 | 
            -
                    ctx = @runner.backend
         | 
| 101 113 | 
             
                    puts <<EOF
         | 
| 102 114 |  | 
| 103 115 | 
             
            Available commands:
         | 
| @@ -110,14 +122,9 @@ Available commands: | |
| 110 122 | 
             
            You can use resources in this environment to test the target machine. For example:
         | 
| 111 123 |  | 
| 112 124 | 
             
                command('uname -a').stdout
         | 
| 113 | 
            -
                file('/proc/cpuinfo').content => "value" | 
| 114 | 
            -
             | 
| 115 | 
            -
            You are currently running on:
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                OS platform:  #{mark ctx.os[:name] || 'unknown'}
         | 
| 118 | 
            -
                OS family:  #{mark ctx.os[:family] || 'unknown'}
         | 
| 119 | 
            -
                OS release: #{mark ctx.os[:release] || 'unknown'}
         | 
| 125 | 
            +
                file('/proc/cpuinfo').content => "value"
         | 
| 120 126 |  | 
| 127 | 
            +
            #{print_target_info}
         | 
| 121 128 | 
             
            EOF
         | 
| 122 129 | 
             
                  elsif resource == 'resources'
         | 
| 123 130 | 
             
                    resources
         | 
    
        data/lib/inspec/version.rb
    CHANGED
    
    
| @@ -0,0 +1,83 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # author: Adam Leff
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'utils/parser'
         | 
| 5 | 
            +
            require 'utils/filter'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Inspec::Resources
         | 
| 8 | 
            +
              class Crontab < Inspec.resource(1)
         | 
| 9 | 
            +
                name 'crontab'
         | 
| 10 | 
            +
                desc 'Use the crontab InSpec audit resource to test the contents of the crontab for a given user which contains information about scheduled tasks owned by that user.'
         | 
| 11 | 
            +
                example "
         | 
| 12 | 
            +
                  describe crontab('root') do
         | 
| 13 | 
            +
                    its('commands') { should include '/path/to/some/script' }
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  describe crontab('myuser').commands('/home/myuser/build.sh') do
         | 
| 17 | 
            +
                    its('hours') { should cmp '*' }
         | 
| 18 | 
            +
                    its('minutes') { should cmp '*' }
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  describe crontab.where({'hour' => '*', 'minute' => '*'}) do
         | 
| 22 | 
            +
                    its('entries.length') { should cmp '0' }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                "
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                attr_reader :params
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                include CommentParser
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def initialize(user = nil)
         | 
| 31 | 
            +
                  @user   = user
         | 
| 32 | 
            +
                  @params = read_crontab
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  return skip_resource 'The `crontab` resource is not supported on your OS.' unless inspec.os.unix?
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def read_crontab
         | 
| 38 | 
            +
                  inspec.command(crontab_cmd).stdout.lines.map { |l| parse_crontab_line(l) }.compact
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def parse_crontab_line(l)
         | 
| 42 | 
            +
                  data, = parse_comment_line(l, comment_char: '#', standalone_comments: false)
         | 
| 43 | 
            +
                  return nil if data.nil? || data.empty?
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  elements = data.split(/\s+/, 6)
         | 
| 46 | 
            +
                  {
         | 
| 47 | 
            +
                    'minute'  => elements.at(0),
         | 
| 48 | 
            +
                    'hour'    => elements.at(1),
         | 
| 49 | 
            +
                    'day'     => elements.at(2),
         | 
| 50 | 
            +
                    'month'   => elements.at(3),
         | 
| 51 | 
            +
                    'weekday' => elements.at(4),
         | 
| 52 | 
            +
                    'command' => elements.at(5),
         | 
| 53 | 
            +
                  }
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def crontab_cmd
         | 
| 57 | 
            +
                  @user.nil? ? 'crontab -l' : "crontab -l -u #{@user}"
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                filter = FilterTable.create
         | 
| 61 | 
            +
                filter.add_accessor(:where)
         | 
| 62 | 
            +
                      .add_accessor(:entries)
         | 
| 63 | 
            +
                      .add(:minutes,  field: 'minute')
         | 
| 64 | 
            +
                      .add(:hours,    field: 'hour')
         | 
| 65 | 
            +
                      .add(:days,     field: 'day')
         | 
| 66 | 
            +
                      .add(:months,   field: 'month')
         | 
| 67 | 
            +
                      .add(:weekdays, field: 'weekday')
         | 
| 68 | 
            +
                      .add(:commands, field: 'command')
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                # rebuild the crontab line from raw content
         | 
| 71 | 
            +
                filter.add(:content) { |t, _|
         | 
| 72 | 
            +
                  t.entries.map do |e|
         | 
| 73 | 
            +
                    [e.minute, e.hour, e.day, e.month, e.weekday, e.command].join(' ')
         | 
| 74 | 
            +
                  end.join("\n")
         | 
| 75 | 
            +
                }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                filter.connect(self, :params)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def to_s
         | 
| 80 | 
            +
                  @user.nil? ? 'crontab for current user' : "crontab for user #{@user}"
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
              end
         | 
| 83 | 
            +
            end
         | 
    
        data/lib/resources/package.rb
    CHANGED
    
    | @@ -29,7 +29,7 @@ module Inspec::Resources | |
| 29 29 | 
             
                  os = inspec.os
         | 
| 30 30 | 
             
                  if os.debian?
         | 
| 31 31 | 
             
                    @pkgman = Deb.new(inspec)
         | 
| 32 | 
            -
                  elsif %w{ | 
| 32 | 
            +
                  elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
         | 
| 33 33 | 
             
                    @pkgman = Rpm.new(inspec)
         | 
| 34 34 | 
             
                  elsif ['arch'].include?(os[:family])
         | 
| 35 35 | 
             
                    @pkgman = Pacman.new(inspec)
         | 
    
        data/lib/resources/packages.rb
    CHANGED
    
    | @@ -23,12 +23,17 @@ module Inspec::Resources | |
| 23 23 | 
             
                "
         | 
| 24 24 |  | 
| 25 25 | 
             
                def initialize(pattern)
         | 
| 26 | 
            -
                   | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 26 | 
            +
                  os = inspec.os
         | 
| 27 | 
            +
                  if os.debian?
         | 
| 28 | 
            +
                    @pkgs = Debs.new(inspec)
         | 
| 29 | 
            +
                  elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
         | 
| 30 | 
            +
                    @pkgs = Rpms.new(inspec)
         | 
| 31 | 
            +
                  else
         | 
| 32 | 
            +
                    return skip_resource "The packages resource is not yet supported on OS #{inspec.os.name}"
         | 
| 33 | 
            +
                  end
         | 
| 29 34 |  | 
| 30 35 | 
             
                  @pattern = pattern_regexp(pattern)
         | 
| 31 | 
            -
                  all_pkgs = build_package_list | 
| 36 | 
            +
                  all_pkgs = @pkgs.build_package_list
         | 
| 32 37 | 
             
                  @list = all_pkgs.find_all do |hm|
         | 
| 33 38 | 
             
                    hm[:name] =~ @pattern
         | 
| 34 39 | 
             
                  end
         | 
| @@ -48,14 +53,6 @@ module Inspec::Resources | |
| 48 53 |  | 
| 49 54 | 
             
                private
         | 
| 50 55 |  | 
| 51 | 
            -
                def packages_command
         | 
| 52 | 
            -
                  os = inspec.os
         | 
| 53 | 
            -
                  if os.debian?
         | 
| 54 | 
            -
                    command = "dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\\n'"
         | 
| 55 | 
            -
                  end
         | 
| 56 | 
            -
                  command
         | 
| 57 | 
            -
                end
         | 
| 58 | 
            -
             | 
| 59 56 | 
             
                def pattern_regexp(p)
         | 
| 60 57 | 
             
                  if p.class == String
         | 
| 61 58 | 
             
                    Regexp.new(Regexp.escape(p))
         | 
| @@ -67,21 +64,48 @@ module Inspec::Resources | |
| 67 64 | 
             
                end
         | 
| 68 65 |  | 
| 69 66 | 
             
                def filtered_packages
         | 
| 70 | 
            -
                  warn "The packages resource is not yet supported on OS #{inspec.os.name}"  | 
| 67 | 
            +
                  warn "The packages resource is not yet supported on OS #{inspec.os.name}" if resource_skipped
         | 
| 71 68 | 
             
                  @list
         | 
| 72 69 | 
             
                end
         | 
| 70 | 
            +
              end
         | 
| 73 71 |  | 
| 74 | 
            -
             | 
| 72 | 
            +
              class PkgsManagement
         | 
| 73 | 
            +
                PackageStruct = Struct.new(:status, :name, :version)
         | 
| 74 | 
            +
                attr_reader :inspec
         | 
| 75 | 
            +
                def initialize(inspec)
         | 
| 76 | 
            +
                  @inspec = inspec
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
              end
         | 
| 75 79 |  | 
| 76 | 
            -
             | 
| 80 | 
            +
              # Debian / Ubuntu
         | 
| 81 | 
            +
              class Debs < PkgsManagement
         | 
| 82 | 
            +
                def build_package_list
         | 
| 83 | 
            +
                  # use two spaces as delimiter in case any of the fields has a space in it
         | 
| 84 | 
            +
                  command = "dpkg-query -W -f='${db:Status-Abbrev}  ${Package}  ${Version}\\n'"
         | 
| 77 85 | 
             
                  cmd = inspec.command(command)
         | 
| 78 | 
            -
                  all = cmd.stdout.split("\n") | 
| 86 | 
            +
                  all = cmd.stdout.split("\n")
         | 
| 79 87 | 
             
                  return [] if all.nil?
         | 
| 80 88 | 
             
                  all.map do |m|
         | 
| 81 | 
            -
                    a = m.split
         | 
| 89 | 
            +
                    a = m.split(/ {2,}/)
         | 
| 82 90 | 
             
                    a[0] = 'installed' if a[0] =~ /^.i/
         | 
| 83 91 | 
             
                    a[2] = a[2].split(':').last
         | 
| 84 | 
            -
                     | 
| 92 | 
            +
                    PackageStruct.new(*a)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
              # RedHat family
         | 
| 98 | 
            +
              class Rpms < PkgsManagement
         | 
| 99 | 
            +
                def build_package_list
         | 
| 100 | 
            +
                  # use two spaces as delimiter in case any of the fields has a space in it
         | 
| 101 | 
            +
                  command = "rpm -qa --queryformat '%{NAME}  %{VERSION}-%{RELEASE}\\n'"
         | 
| 102 | 
            +
                  cmd = inspec.command(command)
         | 
| 103 | 
            +
                  all = cmd.stdout.split("\n")
         | 
| 104 | 
            +
                  return [] if all.nil?
         | 
| 105 | 
            +
                  all.map do |m|
         | 
| 106 | 
            +
                    a = m.split('  ')
         | 
| 107 | 
            +
                    a.unshift('installed')
         | 
| 108 | 
            +
                    PackageStruct.new(*a)
         | 
| 85 109 | 
             
                  end
         | 
| 86 110 | 
             
                end
         | 
| 87 111 | 
             
              end
         | 
| @@ -27,7 +27,7 @@ module SourceReaders | |
| 27 27 |  | 
| 28 28 | 
             
                # This create a new instance of an InSpec profile source reader
         | 
| 29 29 | 
             
                #
         | 
| 30 | 
            -
                # @param [ | 
| 30 | 
            +
                # @param [FileProvider] target An instance of a FileProvider object that can list files and read them
         | 
| 31 31 | 
             
                # @param [String] metadata_source eg. inspec.yml or metadata.rb
         | 
| 32 32 | 
             
                def initialize(target, metadata_source)
         | 
| 33 33 | 
             
                  @target = target
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: inspec
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.15.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Dominik Richter
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2017-02- | 
| 11 | 
            +
            date: 2017-02-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: train
         | 
| @@ -232,6 +232,20 @@ dependencies: | |
| 232 232 | 
             
                - - ">="
         | 
| 233 233 | 
             
                  - !ruby/object:Gem::Version
         | 
| 234 234 | 
             
                    version: 0.9.0
         | 
| 235 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 236 | 
            +
              name: toml
         | 
| 237 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 238 | 
            +
                requirements:
         | 
| 239 | 
            +
                - - "~>"
         | 
| 240 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 241 | 
            +
                    version: '0.1'
         | 
| 242 | 
            +
              type: :runtime
         | 
| 243 | 
            +
              prerelease: false
         | 
| 244 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 245 | 
            +
                requirements:
         | 
| 246 | 
            +
                - - "~>"
         | 
| 247 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 248 | 
            +
                    version: '0.1'
         | 
| 235 249 | 
             
            description: InSpec provides a framework for creating end-to-end infrastructure tests.
         | 
| 236 250 | 
             
              You can use it for integration or even compliance testing. Create fully portable
         | 
| 237 251 | 
             
              test profiles and use them in your workflow to ensure stability and security. Integrate
         | 
| @@ -272,6 +286,7 @@ files: | |
| 272 286 | 
             
            - docs/resources/bridge.md.erb
         | 
| 273 287 | 
             
            - docs/resources/bsd_service.md.erb
         | 
| 274 288 | 
             
            - docs/resources/command.md.erb
         | 
| 289 | 
            +
            - docs/resources/crontab.md.erb
         | 
| 275 290 | 
             
            - docs/resources/csv.md.erb
         | 
| 276 291 | 
             
            - docs/resources/directory.md.erb
         | 
| 277 292 | 
             
            - docs/resources/etc_group.md.erb
         | 
| @@ -370,7 +385,7 @@ files: | |
| 370 385 | 
             
            - examples/meta-profile/inspec.lock
         | 
| 371 386 | 
             
            - examples/meta-profile/inspec.yml
         | 
| 372 387 | 
             
            - examples/meta-profile/vendor/3d473e72d8b70018386a53e0a105e92ccbb4115dc268cadc16ff53d550d2898e.tar.gz
         | 
| 373 | 
            -
            - examples/meta-profile/vendor/ | 
| 388 | 
            +
            - examples/meta-profile/vendor/9ad48391d4e6efff0a13d06736c5b075fb021410e0a629e087bc21e9617d957c.tar.gz
         | 
| 374 389 | 
             
            - examples/meta-profile/vendor/e25d521fb1093b4c23b31a7dc8f41b5540236f4a433960b151bc427523662ab6.tar.gz
         | 
| 375 390 | 
             
            - examples/profile-attribute.yml
         | 
| 376 391 | 
             
            - examples/profile-attribute/README.md
         | 
| @@ -380,7 +395,6 @@ files: | |
| 380 395 | 
             
            - examples/profile/controls/example.rb
         | 
| 381 396 | 
             
            - examples/profile/controls/gordon.rb
         | 
| 382 397 | 
             
            - examples/profile/controls/meta.rb
         | 
| 383 | 
            -
            - examples/profile/inspec.lock
         | 
| 384 398 | 
             
            - examples/profile/inspec.yml
         | 
| 385 399 | 
             
            - examples/profile/libraries/gordon_config.rb
         | 
| 386 400 | 
             
            - inspec.gemspec
         | 
| @@ -400,6 +414,10 @@ files: | |
| 400 414 | 
             
            - lib/bundles/inspec-compliance/support.rb
         | 
| 401 415 | 
             
            - lib/bundles/inspec-compliance/target.rb
         | 
| 402 416 | 
             
            - lib/bundles/inspec-compliance/test/integration/default/cli.rb
         | 
| 417 | 
            +
            - lib/bundles/inspec-habitat.rb
         | 
| 418 | 
            +
            - lib/bundles/inspec-habitat/cli.rb
         | 
| 419 | 
            +
            - lib/bundles/inspec-habitat/log.rb
         | 
| 420 | 
            +
            - lib/bundles/inspec-habitat/profile.rb
         | 
| 403 421 | 
             
            - lib/bundles/inspec-init.rb
         | 
| 404 422 | 
             
            - lib/bundles/inspec-init/README.md
         | 
| 405 423 | 
             
            - lib/bundles/inspec-init/cli.rb
         | 
| @@ -474,7 +492,6 @@ files: | |
| 474 492 | 
             
            - lib/inspec/source_reader.rb
         | 
| 475 493 | 
             
            - lib/inspec/version.rb
         | 
| 476 494 | 
             
            - lib/matchers/matchers.rb
         | 
| 477 | 
            -
            - lib/resources/.ssh_conf.rb.swp
         | 
| 478 495 | 
             
            - lib/resources/apache.rb
         | 
| 479 496 | 
             
            - lib/resources/apache_conf.rb
         | 
| 480 497 | 
             
            - lib/resources/apt.rb
         | 
| @@ -485,6 +502,7 @@ files: | |
| 485 502 | 
             
            - lib/resources/bond.rb
         | 
| 486 503 | 
             
            - lib/resources/bridge.rb
         | 
| 487 504 | 
             
            - lib/resources/command.rb
         | 
| 505 | 
            +
            - lib/resources/crontab.rb
         | 
| 488 506 | 
             
            - lib/resources/csv.rb
         | 
| 489 507 | 
             
            - lib/resources/directory.rb
         | 
| 490 508 | 
             
            - lib/resources/etc_group.rb
         | 
| @@ -575,7 +593,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 575 593 | 
             
                  version: '0'
         | 
| 576 594 | 
             
            requirements: []
         | 
| 577 595 | 
             
            rubyforge_project: 
         | 
| 578 | 
            -
            rubygems_version: 2.5. | 
| 596 | 
            +
            rubygems_version: 2.5.1
         | 
| 579 597 | 
             
            signing_key: 
         | 
| 580 598 | 
             
            specification_version: 4
         | 
| 581 599 | 
             
            summary: Infrastructure and compliance testing.
         | 
| Binary file | 
| Binary file |