From 0e187f6117f67823d46c30cd38192f03d2f6ce9b Mon Sep 17 00:00:00 2001 From: Ryan Larson Date: Wed, 29 Mar 2017 09:42:24 -0700 Subject: [PATCH] Feature/fix ability to pass in supermarket url (#1595) * Enable customization of supermarket_url It looks like this was originally supposed to work, but at some point the default value was put in the method body rather than in the method parameters. This change allows you to configure the supermarket_url in test kitchen like so: ``` verifier: inspec_tests: - name: linux-hardening supermarket: som3guy/apache-disa-stig supermarket_url: https://my.supermarket.com ``` Signed-off-by: Ryan Larson --- inspec.gemspec | 1 + lib/bundles/inspec-supermarket/api.rb | 10 +- .../bundles/inspec-supermarket/api_test.rb | 131 ++++++++++++++++++ 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 test/unit/bundles/inspec-supermarket/api_test.rb diff --git a/inspec.gemspec b/inspec.gemspec index 0ee5c7efa..4c2641b13 100644 --- a/inspec.gemspec +++ b/inspec.gemspec @@ -42,4 +42,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'nokogiri', '~> 1.6' spec.add_dependency 'faraday', '>=0.9.0' spec.add_dependency 'toml', '~> 0.1' + spec.add_dependency 'addressable', '~> 2.5' end diff --git a/lib/bundles/inspec-supermarket/api.rb b/lib/bundles/inspec-supermarket/api.rb index 1abc20843..0ba71ab59 100644 --- a/lib/bundles/inspec-supermarket/api.rb +++ b/lib/bundles/inspec-supermarket/api.rb @@ -4,6 +4,7 @@ # author: Dominik Richter require 'net/http' +require 'addressable/uri' module Supermarket class API @@ -26,9 +27,10 @@ module Supermarket end def self.profile_name(profile) - uri = URI(profile) + # We use Addressable::URI here because URI has a bug in Ruby 2.1.x where it doesn't allow underscore in host + uri = Addressable::URI.parse profile [uri.host, uri.path[1..-1]] - rescue URI::Error => _e + rescue nil end @@ -50,8 +52,8 @@ module Supermarket supermarket_tool['tool_owner'] == tool_owner && supermarket_tool['tool'] == tool end - def self.find(profile, supermarket_url) - profiles = Supermarket::API.profiles(supermarket_url=SUPERMARKET_URL) + def self.find(profile, supermarket_url = SUPERMARKET_URL) + profiles = Supermarket::API.profiles(supermarket_url) if !profiles.empty? index = profiles.index { |t| same?(profile, t, supermarket_url) } # return profile or nil diff --git a/test/unit/bundles/inspec-supermarket/api_test.rb b/test/unit/bundles/inspec-supermarket/api_test.rb new file mode 100644 index 000000000..e38d8640e --- /dev/null +++ b/test/unit/bundles/inspec-supermarket/api_test.rb @@ -0,0 +1,131 @@ +require 'helper' +require 'inspec-supermarket/api' + +def default_url?(supermarket_url) + supermarket_url == Supermarket::API::SUPERMARKET_URL +end + +describe Supermarket::API do + let(:subject) { Supermarket::API } + + [Supermarket::API::SUPERMARKET_URL, 'https://my.custom.supermarket'].each do |supermarket_url| + + describe "With #{default_url?(supermarket_url) ? 'default' : supermarket_url} Supermarket URL" do + + let(:profile_search_response_body) do + { + 'start' => 0, + 'total' => 1, + 'items' => [ + { + 'tool_name' => 'test_name', + 'tool_type' => 'compliance_profile', + 'tool_source_url' => supermarket_url, + 'tool_description' => 'test_description', + 'tool_owner' => 'test_owner', + 'tool' => "#{supermarket_url}/api/v1/tools/test_name" + } + ] + } + end + + let(:profile_name) { 'supermarket://test_owner/test_name' } + + describe '#profiles' do + it 'returns the profile list' do + stub_request(:get, "#{supermarket_url}/api/v1/tools-search?items=100&type=compliance_profile"). + to_return(:status => 200, :body => profile_search_response_body.to_json) + test_profile = default_url?(supermarket_url) ? subject.profiles.first : subject.profiles(supermarket_url).first + + test_profile.must_equal(profile_search_response_body['items'].first.merge({'slug' => 'test_name'})) + end + end + + describe '#profile_name' do + it 'returns the profile name and owner from a supermarket://owner/name path' do + tool_owner, tool_name = subject.profile_name('supermarket://test_tool_owner/test_tool_name') + + tool_owner.must_equal('test_tool_owner') + tool_name.must_equal('test_tool_name') + end + end + + describe '#info' do + let(:profile_list_response_body) do + { + 'name' => 'test_name', + 'slug' => 'test_slug', + 'type' => 'test_type', + 'source_url' => supermarket_url, + 'description' => 'test_description', + 'instructions' => 'test_instructions', + 'owner' => 'test_owner' + } + end + + it 'returns profile info' do + stub_request(:get, "#{supermarket_url}/api/v1/tools/test_name"). + to_return(:status => 200, :body => profile_list_response_body.to_json) + + profile_info = default_url?(supermarket_url) ? subject.info('test_owner/test_name') : subject.info('test_owner/test_name', supermarket_url) + + profile_info.must_equal(profile_list_response_body) + end + end + + describe '#same?' do + let(:tool_url) { "#{supermarket_url}/api/v1/tools/test_name" } + + it 'is the same on a match' do + supermarket_tool = {'tool_owner' => 'test_owner', 'tool' => tool_url} + same = default_url?(supermarket_url) ? subject.same?(profile_name, supermarket_tool) : subject.same?(profile_name, supermarket_tool, supermarket_url) + same.must_equal(true) + end + + it 'is not the same on a mismatched owner' do + supermarket_tool = {'tool_owner' => 'wrong_owner', 'tool' => tool_url} + same = default_url?(supermarket_url) ? subject.same?(profile_name, supermarket_tool) : subject.same?(profile_name, supermarket_tool, supermarket_url) + same.must_equal(false) + end + + it 'is not the same on a mismatched supermarket tool' do + supermarket_tool = {'tool_owner' => 'test_owner', 'tool' => 'garbage'} + same = default_url?(supermarket_url) ? subject.same?(profile_name, supermarket_tool) : subject.same?(profile_name, supermarket_tool, supermarket_url) + same.must_equal(false) + end + end + + describe '#find' do + let(:empty_profile_search_response_body) do + {start: 0, total: 0, items: []} + end + + it 'returns nil if profiles are empty' do + stub_request(:get, "#{supermarket_url}/api/v1/tools-search?items=100&type=compliance_profile"). + to_return(:status => 200, :body => empty_profile_search_response_body.to_json) + + search = default_url?(supermarket_url) ? subject.find(profile_name) : subject.find(profile_name, supermarket_url) + search.must_be_nil + end + + it 'returns nil if profile not found' do + stub_request(:get, "#{supermarket_url}/api/v1/tools-search?items=100&type=compliance_profile"). + to_return(:status => 200, :body => profile_search_response_body.to_json) + + profile_name_cant_find = 'supermarket://cant_find/not_found' + search = default_url?(supermarket_url) ? subject.find(profile_name_cant_find) : subject.find(profile_name_cant_find, supermarket_url) + search.must_be_nil + end + + it 'returns profile if it is found' do + stub_request(:get, "#{supermarket_url}/api/v1/tools-search?items=100&type=compliance_profile"). + to_return(:status => 200, :body => profile_search_response_body.to_json) + + profile = default_url?(supermarket_url) ? subject.find(profile_name) : subject.find(profile_name, supermarket_url) + + profile.must_equal(profile_search_response_body['items'].first.merge({'slug' => 'test_name'})) + end + end + end + end +end