lib/bundles/inspec-compliance/target.rb in inspec-2.2.70 vs lib/bundles/inspec-compliance/target.rb in inspec-2.2.78

- old
+ new

@@ -11,62 +11,93 @@ # similar to `inspec exec http://localhost:2134/owners/%base%/compliance/%ssh%/tar --user %token%` module Compliance class Fetcher < Fetchers::Url name 'compliance' priority 500 - def self.resolve(target) # rubocop:disable PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize - uri = if target.is_a?(String) && URI(target).scheme == 'compliance' - URI(target) - elsif target.respond_to?(:key?) && target.key?(:compliance) - URI("compliance://#{target[:compliance]}") - end + attr_reader :upstream_sha256 - return nil if uri.nil? + def initialize(target, opts) + super(target, opts) + if target.is_a?(Hash) && target.key?(:url) + @target = target[:url] + @upstream_sha256 = target[:sha256] + elsif target.is_a?(String) + @target = target + @upstream_sha256 = '' + end + end - # we have detailed information available in our lockfile, no need to ask the server - if target.respond_to?(:key?) && target.key?(:url) - profile_fetch_url = target[:url] - config = {} - else - # check if we have a compliance token - config = Compliance::Configuration.new - if config['token'].nil? && config['refresh_token'].nil? - if config['server_type'] == 'automate' - server = 'automate' - msg = 'inspec compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN' - elsif config['server_type'] == 'automate2' - server = 'automate2' - msg = 'inspec compliance login https://your_automate2_server --user USER --token APITOKEN' - else - server = 'compliance' - msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' " - end - raise Inspec::FetcherFailure, <<~EOF + def sha256 + upstream_sha256.empty? ? super : upstream_sha256 + end - Cannot fetch #{uri} because your #{server} token has not been - configured. + def self.check_compliance_token(config) + if config['token'].nil? && config['refresh_token'].nil? + if config['server_type'] == 'automate' + server = 'automate' + msg = 'inspec compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN' + elsif config['server_type'] == 'automate2' + server = 'automate2' + msg = 'inspec compliance login https://your_automate2_server --user USER --token APITOKEN' + else + server = 'compliance' + msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' " + end + raise Inspec::FetcherFailure, <<~EOF - Please login using + Cannot fetch #{uri} because your #{server} token has not been + configured. - #{msg} - EOF - end + Please login using + #{msg} + EOF + end + end + + def self.get_target_uri(target) + if target.is_a?(String) && URI(target).scheme == 'compliance' + URI(target) + elsif target.respond_to?(:key?) && target.key?(:compliance) + URI("compliance://#{target[:compliance]}") + end + end + + def self.resolve(target) + uri = get_target_uri(target) + return nil if uri.nil? + + config = Compliance::Configuration.new + profile = Compliance::API.sanitize_profile_name(uri) + profile_fetch_url = Compliance::API.target_url(config, profile) + # we have detailed information available in our lockfile, no need to ask the server + if target.respond_to?(:key?) && target.key?(:sha256) + profile_checksum = target[:sha256] + else + check_compliance_token(config) # verifies that the target e.g base/ssh exists - profile = Compliance::API.sanitize_profile_name(uri) - if !Compliance::API.exist?(config, profile) + # Call profiles directly instead of exist? to capture the results + # so we can access the upstream sha256 from the results. + _msg, profile_result = Compliance::API.profiles(config, profile) + if profile_result.empty? raise Inspec::FetcherFailure, "The compliance profile #{profile} was not found on the configured compliance server" + else + # Guarantee sorting by verison and grab the latest. + # If version was specified, it will be the first and only result. + # Note we are calling the sha256 as a string, not a symbol since + # it was returned as json from the Compliance API. + profile_info = profile_result.sort_by { |x| Gem::Version.new(x['version']) }[0] + profile_checksum = profile_info.key?('sha256') ? profile_info['sha256'] : '' end - profile_fetch_url = Compliance::API.target_url(config, profile) end # We need to pass the token to the fetcher config['token'] = Compliance::API.get_token(config) # Needed for automate2 post request profile_stub = profile || target[:compliance] config['profile'] = Compliance::API.profile_split(profile_stub) - new(profile_fetch_url, config) + new({ url: profile_fetch_url, sha256: profile_checksum }, config) rescue URI::Error => _e nil end # We want to save compliance: in the lockfile rather than url: to