diff --git a/common/lib/dependabot/dependency.rb b/common/lib/dependabot/dependency.rb index e9e3908c224..493dc59c5d1 100644 --- a/common/lib/dependabot/dependency.rb +++ b/common/lib/dependabot/dependency.rb @@ -118,7 +118,7 @@ def humanized_previous_version return ref_changed? ? previous_ref : nil end - if previous_version.match?(/^[0-9a-f]{40}$/) + if previous_version.match?(/^[0-9a-f]{40}/) return previous_ref if ref_changed? && previous_ref "`#{previous_version[0..6]}`" @@ -134,7 +134,7 @@ def humanized_previous_version def humanized_version return if removed? - if version.match?(/^[0-9a-f]{40}$/) + if version.match?(/^[0-9a-f]{40}/) return new_ref if ref_changed? && new_ref "`#{version[0..6]}`" diff --git a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb index 9c2f07e1996..445a44a4c5d 100644 --- a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb +++ b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb @@ -431,7 +431,7 @@ def commits_details(base:, head:) groups: [], source: { type: "digest", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8d" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8d" \ "fc38288cf73aa07485005" } }], @@ -441,7 +441,7 @@ def commits_details(base:, head:) groups: [], source: { type: "digest", - digest: "sha256:2167a21baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ + digest: "2167a21baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \ "aaaaaaaaaaaaaaaaaaaaa" } }] diff --git a/docker/lib/dependabot/docker/file_parser.rb b/docker/lib/dependabot/docker/file_parser.rb index 01c46854672..6520f5a13d7 100644 --- a/docker/lib/dependabot/docker/file_parser.rb +++ b/docker/lib/dependabot/docker/file_parser.rb @@ -6,7 +6,6 @@ require "dependabot/file_parsers" require "dependabot/file_parsers/base" require "dependabot/errors" -require "dependabot/docker/utils/credentials_finder" module Dependabot module Docker @@ -25,15 +24,15 @@ class FileParser < Dependabot::FileParsers::Base FROM = /FROM/i PLATFORM = /--platform\=(?\S+)/ TAG = /:(?[\w][\w.-]{0,127})/ - DIGEST = /@(?[^\s]+)/ + DIGEST = /(?[0-9a-f]{64})/ NAME = /\s+AS\s+(?[\w-]+)/ FROM_LINE = %r{^#{FROM}\s+(#{PLATFORM}\s+)?(#{REGISTRY}/)? - #{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x + #{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x AWS_ECR_URL = /dkr\.ecr\.(?[^.]+)\.amazonaws\.com/ - IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x + IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x def parse dependency_set = DependencySet.new @@ -77,13 +76,7 @@ def dockerfiles end def version_from(parsed_from_line) - return parsed_from_line.fetch("tag") if parsed_from_line.fetch("tag") - - version_from_digest( - registry: parsed_from_line.fetch("registry"), - image: parsed_from_line.fetch("image"), - digest: parsed_from_line.fetch("digest") - ) + parsed_from_line.fetch("tag") || parsed_from_line.fetch("digest") end def source_from(parsed_from_line) @@ -98,59 +91,6 @@ def source_from(parsed_from_line) source end - def version_from_digest(registry:, image:, digest:) - return unless digest - - registry_details = fetch_registry_details(registry) - repo = docker_repo_name(image, registry_details["registry"]) - client = docker_registry_client(registry_details["registry"], registry_details["credentials"]) - client.tags(repo, auto_paginate: true).fetch("tags").find do |tag| - digest == client.digest(repo, tag) - rescue DockerRegistry2::NotFound - # Shouldn't happen, but it does. Example of existing tag with - # no manifest is "library/python", "2-windowsservercore". - false - end - rescue DockerRegistry2::RegistryAuthenticationException, - RestClient::Forbidden - raise PrivateSourceAuthenticationFailure, registry_details["registry"] - rescue RestClient::Exceptions::OpenTimeout, - RestClient::Exceptions::ReadTimeout - raise if credentials_finder.using_dockerhub?(registry_details["registry"]) - - raise PrivateSourceTimedOut, registry_details["registry"] - end - - def docker_repo_name(image, registry) - return image if image.include? "/" - return "library/#{image}" if credentials_finder.using_dockerhub?(registry) - - image - end - - def docker_registry_client(registry_hostname, registry_credentials) - unless credentials_finder.using_dockerhub?(registry_hostname) - return DockerRegistry2::Registry.new("https://#{registry_hostname}") - end - - DockerRegistry2::Registry.new( - "https://#{registry_hostname}", - user: registry_credentials&.fetch("username", nil), - password: registry_credentials&.fetch("password", nil), - read_timeout: 10 - ) - end - - def fetch_registry_details(registry) - registry ||= credentials_finder.base_registry - credentials = credentials_finder.credentials_for_registry(registry) - { "registry" => registry, "credentials" => credentials } - end - - def credentials_finder - @credentials_finder ||= Utils::CredentialsFinder.new(credentials) - end - def check_required_files # Just check if there are any files at all. return if dependency_files.any? diff --git a/docker/lib/dependabot/docker/file_updater.rb b/docker/lib/dependabot/docker/file_updater.rb index d213b33bb79..25e4b757c43 100644 --- a/docker/lib/dependabot/docker/file_updater.rb +++ b/docker/lib/dependabot/docker/file_updater.rb @@ -82,11 +82,11 @@ def update_digest_and_tag(previous_content, old_source, new_source) old_tag = old_source[:tag] new_tag = new_source[:tag] - old_declaration_regex = /^#{FROM_REGEX}\s+.*@#{old_digest}/ + old_declaration_regex = /^#{FROM_REGEX}\s+.*@sha256:#{old_digest}/ previous_content.gsub(old_declaration_regex) do |old_dec| old_dec. - gsub("@#{old_digest}", "@#{new_digest}"). + gsub("@sha256:#{old_digest}", "@sha256:#{new_digest}"). gsub(":#{old_tag}", ":#{new_tag}") end end @@ -181,7 +181,7 @@ def update_image(file, content) def new_yaml_image(file) element = dependency.requirements.find { |r| r[:file] == file.name } prefix = element.fetch(:source)[:registry] ? "#{element.fetch(:source)[:registry]}/" : "" - digest = element.fetch(:source)[:digest] ? "@#{element.fetch(:source)[:digest]}" : "" + digest = element.fetch(:source)[:digest] ? "@sha256:#{element.fetch(:source)[:digest]}" : "" tag = element.fetch(:source)[:tag] ? ":#{element.fetch(:source)[:tag]}" : "" "#{prefix}#{dependency.name}#{tag}#{digest}" end @@ -194,7 +194,7 @@ def new_yaml_tag(file) def old_yaml_images(file) previous_requirements(file).map do |r| prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : "" - digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : "" + digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : "" tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : "" "#{prefix}#{dependency.name}#{tag}#{digest}" end diff --git a/docker/lib/dependabot/docker/tag.rb b/docker/lib/dependabot/docker/tag.rb index 7c307fe4883..d2b8813beb3 100644 --- a/docker/lib/dependabot/docker/tag.rb +++ b/docker/lib/dependabot/docker/tag.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "dependabot/docker/file_parser" + module Dependabot module Docker class Tag @@ -24,6 +26,10 @@ def to_s name end + def digest? + name.match?(FileParser::DIGEST) + end + def comparable? name.match?(NAME_WITH_VERSION) end diff --git a/docker/lib/dependabot/docker/update_checker.rb b/docker/lib/dependabot/docker/update_checker.rb index ab46432d82b..32a0ddb0c29 100644 --- a/docker/lib/dependabot/docker/update_checker.rb +++ b/docker/lib/dependabot/docker/update_checker.rb @@ -6,6 +6,7 @@ require "dependabot/update_checkers/base" require "dependabot/errors" require "dependabot/docker/tag" +require "dependabot/docker/file_parser" require "dependabot/docker/version" require "dependabot/docker/requirement" require "dependabot/docker/utils/credentials_finder" @@ -106,9 +107,10 @@ def latest_tag_from(version) @tags[version] = fetch_latest_tag(Tag.new(version)) end - # NOTE: It's important that this *always* returns a version (even if + # NOTE: It's important that this *always* returns a tag (even if # it's the existing one) as it is what we later check the digest of. def fetch_latest_tag(version_tag) + return Tag.new(latest_digest) if version_tag.digest? return version_tag unless version_tag.comparable? # Prune out any downgrade tags before checking for pre-releases @@ -164,9 +166,10 @@ def comparable_tags_from_registry(original_tag) end def remove_version_downgrades(candidate_tags, version_tag) + current_version = comparable_version_from(version_tag) + candidate_tags.select do |tag| - comparable_version_from(tag) >= - comparable_version_from(version_tag) + comparable_version_from(tag) >= current_version end end @@ -198,7 +201,11 @@ def version_of_latest_tag end def updated_digest - @updated_digest ||= digest_of(latest_version) + @updated_digest ||= if latest_tag_from(dependency.version).digest? + latest_digest + else + digest_of(latest_version) + end end def tags_from_registry @@ -234,20 +241,18 @@ def digest_of(tag) @digests ||= {} return @digests[tag] if @digests.key?(tag) - @digests[tag] = - begin - docker_registry_client.digest(docker_repo_name, tag) - rescue *transient_docker_errors => e - attempt ||= 1 - attempt += 1 - if attempt > 3 && e.is_a?(DockerRegistry2::NotFound) - nil - else - raise if attempt > 3 + @digests[tag] = fetch_digest_of(tag) + end - retry - end - end + def fetch_digest_of(tag) + docker_registry_client.digest(docker_repo_name, tag)&.delete_prefix("sha256:") + rescue *transient_docker_errors => e + attempt ||= 1 + attempt += 1 + return if attempt > 3 && e.is_a?(DockerRegistry2::NotFound) + raise if attempt > 3 + + retry rescue DockerRegistry2::RegistryAuthenticationException, RestClient::Forbidden raise PrivateSourceAuthenticationFailure, registry_hostname diff --git a/docker/spec/dependabot/docker/file_parser_spec.rb b/docker/spec/dependabot/docker/file_parser_spec.rb index cf26e0db77b..c0d21ed2781 100644 --- a/docker/spec/dependabot/docker/file_parser_spec.rb +++ b/docker/spec/dependabot/docker/file_parser_spec.rb @@ -136,14 +136,14 @@ requirement: nil, groups: [], file: "Dockerfile", - source: { digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" } + source: { digest: "18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" } }] end it "has the right details" do expect(dependency).to be_a(Dependabot::Dependency) expect(dependency.name).to eq("my-fork/ubuntu") - expect(dependency.version).to eq("12.04.5") + expect(dependency.version).to eq("18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005") expect(dependency.requirements).to eq(expected_requirements) end end @@ -228,7 +228,7 @@ and_return(status: 200, body: "", headers: digest_headers) end - its(:length) { is_expected.to eq(0) } + its(:length) { is_expected.to eq(1) } end context "that matches a tag" do @@ -250,7 +250,7 @@ groups: [], file: "Dockerfile", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8d" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8d" \ "fc38288cf73aa07485005" } }] @@ -259,197 +259,11 @@ it "has the right details" do expect(dependency).to be_a(Dependabot::Dependency) expect(dependency.name).to eq("ubuntu") - expect(dependency.version).to eq("12.04.5") + expect(dependency.version).to eq("18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005") expect(dependency.requirements).to eq(expected_requirements) end end - context "for a private registry" do - let(:dockerfile_fixture_name) { "private_digest" } - let(:repo_url) { "https://registry-host.io:5000/v2/myreg/ubuntu/" } - - context "without no/bad authentication credentials" do - before do - tags_url = repo_url + "tags/list" - stub_request(:get, tags_url). - and_return( - status: 401, - body: "", - headers: { "www_authenticate" => "basic 123" } - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { parser.parse }. - to raise_error(error_class) do |error| - expect(error.source).to eq("registry-host.io:5000") - end - end - end - - context "with good authentication credentials" do - let(:parser) do - described_class.new( - dependency_files: files, - credentials: credentials, - source: source - ) - end - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000", - "username" => "grey", - "password" => "pa55word" - }] - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "Dockerfile", - source: { - registry: "registry-host.io:5000", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("myreg/ubuntu") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - - context "that don't include a username and password" do - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000" - }] - end - - its(:length) { is_expected.to eq(1) } - end - end - - context "that uses Amazon ECR" do - let(:dockerfile_fixture_name) { "private_ecr_digest" } - let(:repo_url) do - "https://695729449481.dkr.ecr.eu-west-2.amazonaws.com/v2/" \ - "docker-php/" - end - - context "without credentials" do - before do - tags_url = repo_url + "tags/list" - stub_request(:get, tags_url). - and_return( - status: 401, - body: "", - headers: { "www_authenticate" => "basic 123" } - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { parser.parse }. - to raise_error(error_class) do |error| - expect(error.source). - to eq("695729449481.dkr.ecr.eu-west-2.amazonaws.com") - end - end - end - - context "with credentials" do - let(:parser) do - described_class.new( - dependency_files: files, - credentials: credentials, - source: source - ) - end - - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - "username" => "grey", - "password" => "pa55word" - }] - end - - context "that are invalid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 403, - body: fixture("docker", "ecr_responses", "invalid_token") - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { parser.parse }. - to raise_error(error_class) do |error| - expect(error.source). - to eq("695729449481.dkr.ecr.eu-west-2.amazonaws.com") - end - end - end - - context "that are valid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 200, - body: fixture("docker", "ecr_responses", "auth_data") - ) - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "Dockerfile", - source: { - registry: - "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("docker-php") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - end - end - end - end - context "with replaces-base" do let(:dockerfile_fixture_name) { "digest" } let(:repo_url) { "https://registry-host.io:5000/v2/ubuntu/" } @@ -495,7 +309,7 @@ groups: [], file: "Dockerfile", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8d" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8d" \ "fc38288cf73aa07485005" } }] @@ -504,209 +318,11 @@ it "has the right details" do expect(dependency).to be_a(Dependabot::Dependency) expect(dependency.name).to eq("ubuntu") - expect(dependency.version).to eq("12.04.5") + expect(dependency.version).to eq("18305429afa14ea462f810146ba44d4363ae76e4c8d" \ + "fc38288cf73aa07485005") expect(dependency.requirements).to eq(expected_requirements) end end - - context "when replaces-base is false that uses Amazon ECR" do - let(:dockerfile_fixture_name) { "private_ecr_digest" } - let(:repo_url) do - "https://695729449481.dkr.ecr.eu-west-2.amazonaws.com/v2/" \ - "docker-php/" - end - - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 200, - body: fixture("docker", "ecr_responses", "auth_data") - ) - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "Dockerfile", - source: { - registry: "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("docker-php") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - end - end - - context "when replaces-base set to true and with good authentication credentials" do - let(:parser) do - described_class.new( - dependency_files: files, - credentials: credentials, - source: source - ) - end - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000", - "username" => "grey", - "password" => "pa55word", - "replaces-base" => true - }] - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "Dockerfile", - source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("ubuntu") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - - context "that don't include a username and password" do - before do - tags_url = repo_url + "tags/list" - stub_request(:get, tags_url). - and_return( - status: 401, - body: "", - headers: { "www_authenticate" => "basic 123" } - ) - end - - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000", - "replaces-base" => true - }] - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { parser.parse }. - to raise_error(error_class) do |error| - expect(error.source).to eq("registry-host.io:5000") - end - end - end - end - - context "when replaces-base set to true that uses Amazon ECR" do - let(:dockerfile_fixture_name) { "ecr_digest" } - let(:repo_url) do - "https://695729449481.dkr.ecr.eu-west-2.amazonaws.com/v2/" \ - "docker-php/" - end - - context "with credentials" do - let(:parser) do - described_class.new( - dependency_files: files, - credentials: credentials, - source: source - ) - end - - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - "username" => "grey", - "password" => "pa55word", - "replaces-base" => true - }] - end - - context "that are invalid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 403, - body: fixture("docker", "ecr_responses", "invalid_token") - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { parser.parse }. - to raise_error(error_class) do |error| - expect(error.source). - to eq("695729449481.dkr.ecr.eu-west-2.amazonaws.com") - end - end - end - - context "that are valid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 200, - body: fixture("docker", "ecr_responses", "auth_data") - ) - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "Dockerfile", - source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("docker-php") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - end - end end end end @@ -745,7 +361,7 @@ file: "Dockerfile", source: { tag: "12.04.5", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" } }]) end @@ -1118,7 +734,7 @@ and_return(status: 200, body: "", headers: digest_headers) end - its(:length) { is_expected.to eq(0) } + its(:length) { is_expected.to eq(1) } end context "that matches a tag" do @@ -1140,7 +756,7 @@ groups: [], file: "digest.yaml", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8d" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8d" \ "fc38288cf73aa07485005" } }] @@ -1149,196 +765,10 @@ it "has the right details" do expect(dependency).to be_a(Dependabot::Dependency) expect(dependency.name).to eq("ubuntu") - expect(dependency.version).to eq("12.04.5") + expect(dependency.version).to eq("18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005") expect(dependency.requirements).to eq(expected_requirements) end end - - context "for a private registry" do - let(:podfile_fixture_name) { "private_digest.yaml" } - let(:repo_url) { "https://registry-host.io:5000/v2/myreg/ubuntu/" } - - context "without no/bad authentication credentials" do - before do - tags_url = repo_url + "tags/list" - stub_request(:get, tags_url). - and_return( - status: 401, - body: "", - headers: { "www_authenticate" => "basic 123" } - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { yaml_parser.parse }. - to raise_error(error_class) do |error| - expect(error.source).to eq("registry-host.io:5000") - end - end - end - - context "with good authentication credentials" do - let(:yaml_parser) do - described_class.new( - dependency_files: podfiles, - credentials: credentials, - source: source - ) - end - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000", - "username" => "grey", - "password" => "pa55word" - }] - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "private_digest.yaml", - source: { - registry: "registry-host.io:5000", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("myreg/ubuntu") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - - context "that don't include a username and password" do - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "registry-host.io:5000" - }] - end - - its(:length) { is_expected.to eq(1) } - end - end - - context "that uses Amazon ECR" do - let(:podfile_fixture_name) { "private_ecr_digest.yaml" } - let(:repo_url) do - "https://695729449481.dkr.ecr.eu-west-2.amazonaws.com/v2/" \ - "docker-php/" - end - - context "without credentials" do - before do - tags_url = repo_url + "tags/list" - stub_request(:get, tags_url). - and_return( - status: 401, - body: "", - headers: { "www_authenticate" => "basic 123" } - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { yaml_parser.parse }. - to raise_error(error_class) do |error| - expect(error.source). - to eq("695729449481.dkr.ecr.eu-west-2.amazonaws.com") - end - end - end - - context "with credentials" do - let(:yaml_parser) do - described_class.new( - dependency_files: podfiles, - credentials: credentials, - source: source - ) - end - - let(:credentials) do - [{ - "type" => "docker_registry", - "registry" => "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - "username" => "grey", - "password" => "pa55word" - }] - end - - context "that are invalid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 403, - body: fixture("docker", "ecr_responses", "invalid_token") - ) - end - - it "raises a PrivateSourceAuthenticationFailure error" do - error_class = Dependabot::PrivateSourceAuthenticationFailure - expect { yaml_parser.parse }. - to raise_error(error_class) do |error| - expect(error.source). - to eq("695729449481.dkr.ecr.eu-west-2.amazonaws.com") - end - end - end - - context "that are valid" do - before do - stub_request( - :post, - "https://api.ecr.eu-west-2.amazonaws.com/" - ).and_return( - status: 200, - body: fixture("docker", "ecr_responses", "auth_data") - ) - end - - its(:length) { is_expected.to eq(1) } - - describe "the first dependency" do - subject(:dependency) { dependencies.first } - let(:expected_requirements) do - [{ - requirement: nil, - groups: [], - file: "private_ecr_digest.yaml", - source: { - registry: - "695729449481.dkr.ecr.eu-west-2.amazonaws.com", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76" \ - "e4c8dfc38288cf73aa07485005" - } - }] - end - - it "has the right details" do - expect(dependency).to be_a(Dependabot::Dependency) - expect(dependency.name).to eq("docker-php") - expect(dependency.version).to eq("12.04.5") - expect(dependency.requirements).to eq(expected_requirements) - end - end - end - end - end - end end end @@ -1375,7 +805,7 @@ file: "digest_and_tag.yaml", source: { tag: "12.04.5", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" } }]) end diff --git a/docker/spec/dependabot/docker/file_updater_spec.rb b/docker/spec/dependabot/docker/file_updater_spec.rb index 3df0bd4991a..a3ff18bdb7a 100644 --- a/docker/spec/dependabot/docker/file_updater_spec.rb +++ b/docker/spec/dependabot/docker/file_updater_spec.rb @@ -340,7 +340,7 @@ file: "Dockerfile", source: { tag: "17.10", - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -350,7 +350,7 @@ file: "Dockerfile", source: { tag: "12.04.5", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], @@ -394,7 +394,7 @@ file: "Dockerfile", source: { registry: "registry-host.io:5000", - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -404,7 +404,7 @@ file: "Dockerfile", source: { registry: "registry-host.io:5000", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], @@ -450,7 +450,7 @@ groups: [], file: "Dockerfile", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }, { @@ -458,7 +458,7 @@ groups: [], file: "custom-name", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608", tag: "17.10" } @@ -468,7 +468,7 @@ groups: [], file: "Dockerfile", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }, { @@ -476,7 +476,7 @@ groups: [], file: "custom-name", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005", tag: "12.04.5" } @@ -511,7 +511,7 @@ groups: [], file: "custom-name", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608", tag: "17.10" } @@ -521,7 +521,7 @@ groups: [], file: "custom-name", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005", tag: "12.04.5" } @@ -883,7 +883,7 @@ groups: [], file: "digest.yaml", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -892,7 +892,7 @@ groups: [], file: "digest.yaml", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], @@ -931,7 +931,7 @@ file: "digest_and_tag.yaml", source: { tag: "17.10", - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -941,7 +941,7 @@ file: "digest_and_tag.yaml", source: { tag: "12.04.5", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], @@ -980,7 +980,7 @@ file: "private_digest.yaml", source: { registry: "registry-host.io:5000", - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -990,7 +990,7 @@ file: "private_digest.yaml", source: { registry: "registry-host.io:5000", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], @@ -1042,7 +1042,7 @@ groups: [], file: "digest.yaml", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }, { @@ -1050,7 +1050,7 @@ groups: [], file: "digest_and_tag.yaml", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608", tag: "17.10" } @@ -1060,7 +1060,7 @@ groups: [], file: "digest.yaml", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }, { @@ -1068,7 +1068,7 @@ groups: [], file: "digest_and_tag.yaml", source: { - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005", tag: "12.04.5" } @@ -1104,7 +1104,7 @@ file: "digest_and_tag.yaml", source: { tag: "17.10", - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }], @@ -1114,7 +1114,7 @@ file: "digest_and_tag.yaml", source: { tag: "12.04.5", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8" \ + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8" \ "dfc38288cf73aa07485005" } }], diff --git a/docker/spec/dependabot/docker/update_checker_spec.rb b/docker/spec/dependabot/docker/update_checker_spec.rb index 3993b91b380..af55931e99d 100644 --- a/docker/spec/dependabot/docker/update_checker_spec.rb +++ b/docker/spec/dependabot/docker/update_checker_spec.rb @@ -133,7 +133,7 @@ def stub_tag_with_no_digest(tag) context "that is up-to-date" do let(:source) do { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86ca97" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86ca97" \ "eba880ebf600d68608" } end @@ -143,6 +143,35 @@ def stub_tag_with_no_digest(tag) end end + context "given a digest only" do + let(:tags_fixture_name) { "ubuntu.json" } + + let(:version) { digest } + let(:source) { { digest: digest } } + + let(:headers_response) do + fixture("docker", "registry_manifest_headers", "generic.json") + end + + before do + stub_request(:head, repo_url + "manifests/latest"). + and_return(status: 200, headers: JSON.parse(headers_response)) + end + + context "that is out-to-date" do + let(:digest) { "c5dcd377b75ca89f40a7b4284c05c58be4cd43d089f83af1333e56bde33d579f" } + + it { is_expected.to be_truthy } + end + + context "that is up-to-date" do + let(:latest_digest) { "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86ca97eba880ebf600d68608" } + let(:digest) { latest_digest } + + it { is_expected.to be_falsy } + end + end + context "when the 'latest' version is just a more precise one" do let(:dependency_name) { "python" } let(:version) { "3.6" } @@ -964,7 +993,7 @@ def stub_tag_with_no_digest(tag) context "when the docker registry only knows about versions older than the current version" do let(:dependency_name) { "jetstack/cert-manager-controller" } let(:version) { "v1.7.2" } - let(:digest) { "sha256:1815870847a48a9a6f177b90005d8df273e79d00830c21af9d43e1b5d8d208b4" } + let(:digest) { "1815870847a48a9a6f177b90005d8df273e79d00830c21af9d43e1b5d8d208b4" } let(:dependency) do Dependabot::Dependency.new( name: dependency_name, @@ -976,7 +1005,7 @@ def stub_tag_with_no_digest(tag) source: { registry: "quay.io", tag: "v1.7.2", - digest: "sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" + digest: "18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005" } }], package_manager: "docker" @@ -1038,7 +1067,7 @@ def stub_tag_with_no_digest(tag) groups: [], file: "Dockerfile", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608" } }] @@ -1064,7 +1093,7 @@ def stub_tag_with_no_digest(tag) groups: [], file: "Dockerfile", source: { - digest: "sha256:3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ + digest: "3ea1ca1aa8483a38081750953ad75046e6cc9f6b86" \ "ca97eba880ebf600d68608", tag: "17.10" } diff --git a/docker/spec/fixtures/docker/dockerfiles/private_ecr_digest b/docker/spec/fixtures/docker/dockerfiles/private_ecr_digest deleted file mode 100644 index c28fbe583da..00000000000 --- a/docker/spec/fixtures/docker/dockerfiles/private_ecr_digest +++ /dev/null @@ -1,19 +0,0 @@ -FROM 695729449481.dkr.ecr.eu-west-2.amazonaws.com/docker-php@sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005 - -### SYSTEM DEPENDENCIES - -RUN apt-get update \ - && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ - build-essential \ - dirmngr \ - git \ - - -### RUBY - -# Install Ruby 2.4 -RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C3173AA6 \ - && echo "deb http://ppa.launchpad.net/brightbox/ruby-ng/ubuntu zesty main" > /etc/apt/sources.list.d/brightbox.list \ - && apt-get update -RUN apt-get install -y ruby2.4 ruby2.4-dev diff --git a/docker/spec/fixtures/kubernetes/yaml/private_ecr_digest.yaml b/docker/spec/fixtures/kubernetes/yaml/private_ecr_digest.yaml deleted file mode 100644 index a2c7dad077a..00000000000 --- a/docker/spec/fixtures/kubernetes/yaml/private_ecr_digest.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: ubuntu -spec: - containers: - - name: ubuntu - image: 695729449481.dkr.ecr.eu-west-2.amazonaws.com/docker-php@sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005 - ports: - - containerPort: 80 \ No newline at end of file