Description
When when the GCP transport is used with an InSpec profile using the file()
resource, InSpec raises an exception and exits.
This comes up frequently as inspec init profile my-profile
generates a simple InSpec control that uses the file()
.
Expected behavior
The file()
resource is built with the assumption that the system under test
is a host, but when the GCP transport is used the semantics of file()
get a
little fuzzy. There are two reasonable scenarios I can think of for expected
behavior:
- Option 1: Read a local file
- Option 2: Exit gracefully
Actual behavior
InSpec assumes that the backend can read files, calls #source_path
on nil
, raises an exception, and crashes.
Steps to reproduce
Example Gemfile:
source 'https://rubygems.org'
gem 'inspec', '~> 2.3.0'
Generate a new InSpec profile:
└> bundle exec inspec init profile gcp-file
Create new profile at /Users/thebo/lagrange/projects/inspec/inspec-gcp-file/gcp-file
* Create directory libraries
* Create file README.md
* Create directory controls
* Create file controls/example.rb
* Create file inspec.yml
* Create file libraries/.gitkeep
Amend the inspec.yml
to match the configuration given in the inspec-gcp README:
name: my-profile
title: My GCP InSpec Profile
version: 0.1.0
inspec_version: '>= 2.2.10'
depends:
- name: inspec-gcp
url: https://github.com/inspec/inspec-gcp/archive/master.tar.gz
supports:
- platform: gcp
Attempt to run the newly generated profile with the gcp transport:
# bundle exec inspec exec gcp-file -t gcp://
Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/.
bundler: failed to load command: inspec (/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/bin/inspec)
NameError: undefined method `source_path' for class `NilClass'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/resources/file.rb:52:in `method'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/resources/file.rb:52:in `block (2 levels) in <class:FileResource>'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/resources/file.rb:137:in `to_s'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/metadata.rb:180:in `build_description_from'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/metadata.rb:133:in `populate'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/metadata.rb:258:in `create'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/example_group.rb:422:in `set_it_up'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/example_group.rb:386:in `subclass'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/rspec-core-3.8.0/lib/rspec/core/example_group.rb:260:in `block in define_example_group_method'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner_rspec.rb:27:in `example_group'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:310:in `rspec_failed_block'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:268:in `get_check_example'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:280:in `block in register_rule'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:279:in `each'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:279:in `flat_map'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:279:in `register_rule'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:96:in `block in load'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:95:in `each'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:95:in `load'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/runner.rb:102:in `run'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/lib/inspec/cli.rb:184:in `exec'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/thor-0.20.0/lib/thor/command.rb:27:in `run'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/thor-0.20.0/lib/thor/invocation.rb:126:in `invoke_command'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/thor-0.20.0/lib/thor.rb:387:in `dispatch'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/thor-0.20.0/lib/thor/base.rb:466:in `start'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/gems/inspec-2.3.10/bin/inspec:12:in `<top (required)>'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/bin/inspec:23:in `load'
/Users/thebo/lagrange/projects/inspec/inspec-gcp-file/.bundle/ruby/2.4.0/bin/inspec:23:in `<top (required)>'
Workarounds
When a local file is under test, the file can be read directly through File.read
or a Pathname
object can be used.
control 'gcp-file-workaround' do
# the `file()` resource fails when the GCP transport is in use; to work around this issue we
# intentionally interact with the local filesystem to perform our tests
describe Pathname.new("credentials.json") do
it { should exist }
it "contains valid JSON" do
JSON.parse(subject.read)
end
end
end
Activity