dependabot-docker 0.77.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f91e012344a707c90832f405af7e3c63084bd319b93c0ae9181867357de02e36
4
+ data.tar.gz: 0ec7fcb7029b52d69b9664f3b67aff22cbee2f9dfd71263c7041ee24dea59a15
5
+ SHA512:
6
+ metadata.gz: f628cee07f0b8346d582ff23496607187bcbbf0dc174798b4d811f1410637c3442888dd41b530d23639e80477c83869272a7ba4543768e43cd93f10b72fce4ca
7
+ data.tar.gz: 52732d1c2c1273a91a8377ae551c9fecae807f460e883f7ab930c3f874dbf86365d4398770a5d7f71889c0e37c13af39bfadee3e6871901bbcfa459dc02cc98f
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/file_fetchers"
4
+ require "dependabot/file_fetchers/base"
5
+
6
+ module Dependabot
7
+ module Docker
8
+ class FileFetcher < Dependabot::FileFetchers::Base
9
+ def self.required_files_in?(filenames)
10
+ filenames.any? { |f| f.match?(/dockerfile/i) }
11
+ end
12
+
13
+ def self.required_files_message
14
+ "Repo must contain a Dockerfile."
15
+ end
16
+
17
+ private
18
+
19
+ def fetch_files
20
+ fetched_files = []
21
+ fetched_files += dockerfiles
22
+
23
+ return fetched_files if fetched_files.any?
24
+
25
+ raise(
26
+ Dependabot::DependencyFileNotFound,
27
+ File.join(directory, "Dockerfile")
28
+ )
29
+ end
30
+
31
+ def dockerfiles
32
+ @dockerfiles ||=
33
+ repo_contents(raise_errors: false).
34
+ select { |f| f.type == "file" && f.name.match?(/dockerfile/i) }.
35
+ map { |f| fetch_file_from_host(f.name) }
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ Dependabot::FileFetchers.
42
+ register("docker", Dependabot::Docker::FileFetcher)
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "docker_registry2"
4
+
5
+ require "dependabot/dependency"
6
+ require "dependabot/file_parsers"
7
+ require "dependabot/file_parsers/base"
8
+ require "dependabot/errors"
9
+ require "dependabot/docker/utils/credentials_finder"
10
+
11
+ module Dependabot
12
+ module Docker
13
+ class FileParser < Dependabot::FileParsers::Base
14
+ require "dependabot/file_parsers/base/dependency_set"
15
+
16
+ # Detials of Docker regular expressions is at
17
+ # https://github.com/docker/distribution/blob/master/reference/regexp.go
18
+ DOMAIN_COMPONENT =
19
+ /(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/.freeze
20
+ DOMAIN = /(?:#{DOMAIN_COMPONENT}(?:\.#{DOMAIN_COMPONENT})+)/.freeze
21
+ REGISTRY = /(?<registry>#{DOMAIN}(?::[0-9]+)?)/.freeze
22
+
23
+ NAME_COMPONENT = /(?:[a-z0-9]+(?:(?:[._]|__|[-]*)[a-z0-9]+)*)/.freeze
24
+ IMAGE = %r{(?<image>#{NAME_COMPONENT}(?:/#{NAME_COMPONENT})*)}.freeze
25
+
26
+ FROM = /[Ff][Rr][Oo][Mm]/.freeze
27
+ TAG = /:(?<tag>[\w][\w.-]{0,127})/.freeze
28
+ DIGEST = /@(?<digest>[^\s]+)/.freeze
29
+ NAME = /\s+AS\s+(?<name>[a-zA-Z0-9_-]+)/.freeze
30
+ FROM_LINE =
31
+ %r{^#{FROM}\s+(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}.freeze
32
+
33
+ AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+).amazonaws\.com/.freeze
34
+
35
+ def parse
36
+ dependency_set = DependencySet.new
37
+
38
+ dockerfiles.each do |dockerfile|
39
+ dockerfile.content.each_line do |line|
40
+ next unless FROM_LINE.match?(line)
41
+
42
+ parsed_from_line = FROM_LINE.match(line).named_captures
43
+
44
+ version = version_from(parsed_from_line)
45
+ next unless version
46
+
47
+ dependency_set << Dependency.new(
48
+ name: parsed_from_line.fetch("image"),
49
+ version: version,
50
+ package_manager: "docker",
51
+ requirements: [
52
+ requirement: nil,
53
+ groups: [],
54
+ file: dockerfile.name,
55
+ source: source_from(parsed_from_line)
56
+ ]
57
+ )
58
+ end
59
+ end
60
+
61
+ dependency_set.dependencies
62
+ end
63
+
64
+ private
65
+
66
+ def dockerfiles
67
+ # The Docker file fetcher only fetches Dockerfiles, so no need to
68
+ # filter here
69
+ dependency_files
70
+ end
71
+
72
+ def version_from(parsed_from_line)
73
+ return parsed_from_line.fetch("tag") if parsed_from_line.fetch("tag")
74
+
75
+ version_from_digest(
76
+ registry: parsed_from_line.fetch("registry"),
77
+ image: parsed_from_line.fetch("image"),
78
+ digest: parsed_from_line.fetch("digest")
79
+ )
80
+ end
81
+
82
+ def source_from(parsed_from_line)
83
+ source = {}
84
+
85
+ if parsed_from_line.fetch("registry")
86
+ source[:registry] = parsed_from_line.fetch("registry")
87
+ end
88
+
89
+ if parsed_from_line.fetch("tag")
90
+ source[:tag] = parsed_from_line.fetch("tag")
91
+ end
92
+
93
+ if parsed_from_line.fetch("digest")
94
+ source[:digest] = parsed_from_line.fetch("digest")
95
+ end
96
+
97
+ source
98
+ end
99
+
100
+ def version_from_digest(registry:, image:, digest:)
101
+ return unless digest
102
+
103
+ repo = docker_repo_name(image, registry)
104
+ registry_client = docker_registry_client(registry)
105
+ registry_client.tags(repo).fetch("tags").find do |tag|
106
+ digest == registry_client.digest(repo, tag)
107
+ rescue DockerRegistry2::NotFound
108
+ # Shouldn't happen, but it does. Example of existing tag with
109
+ # no manifest is "library/python", "2-windowsservercore".
110
+ false
111
+ end
112
+ rescue DockerRegistry2::RegistryAuthenticationException,
113
+ RestClient::Forbidden
114
+ raise if standard_registry?(registry)
115
+
116
+ raise PrivateSourceAuthenticationFailure, registry
117
+ end
118
+
119
+ def docker_repo_name(image, registry)
120
+ return image unless standard_registry?(registry)
121
+ return image unless image.split("/").count < 2
122
+
123
+ "library/#{image}"
124
+ end
125
+
126
+ def docker_registry_client(registry)
127
+ if registry
128
+ credentials = registry_credentials(registry)
129
+
130
+ DockerRegistry2::Registry.new(
131
+ "https://#{registry}",
132
+ user: credentials&.fetch("username"),
133
+ password: credentials&.fetch("password")
134
+ )
135
+ else
136
+ DockerRegistry2::Registry.new("https://registry.hub.docker.com")
137
+ end
138
+ end
139
+
140
+ def registry_credentials(registry_url)
141
+ credentials_finder.credentials_for_registry(registry_url)
142
+ end
143
+
144
+ def credentials_finder
145
+ @credentials_finder ||= Utils::CredentialsFinder.new(credentials)
146
+ end
147
+
148
+ def standard_registry?(registry)
149
+ return true if registry.nil?
150
+
151
+ registry == "registry.hub.docker.com"
152
+ end
153
+
154
+ def check_required_files
155
+ # Just check if there are any files at all.
156
+ return if dependency_files.any?
157
+
158
+ raise "No Dockerfile!"
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ Dependabot::FileParsers.
165
+ register("docker", Dependabot::Docker::FileParser)
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/file_updaters"
4
+ require "dependabot/file_updaters/base"
5
+ require "dependabot/errors"
6
+
7
+ module Dependabot
8
+ module Docker
9
+ class FileUpdater < Dependabot::FileUpdaters::Base
10
+ FROM_REGEX = /[Ff][Rr][Oo][Mm]/.freeze
11
+
12
+ def self.updated_files_regex
13
+ [/dockerfile/]
14
+ end
15
+
16
+ def updated_dependency_files
17
+ updated_files = []
18
+
19
+ dependency_files.each do |file|
20
+ next unless requirement_changed?(file, dependency)
21
+
22
+ updated_files <<
23
+ updated_file(
24
+ file: file,
25
+ content: updated_dockerfile_content(file)
26
+ )
27
+ end
28
+
29
+ updated_files.reject! { |f| dependency_files.include?(f) }
30
+ raise "No files changed!" if updated_files.none?
31
+
32
+ updated_files
33
+ end
34
+
35
+ private
36
+
37
+ def dependency
38
+ # Dockerfiles will only ever be updating a single dependency
39
+ dependencies.first
40
+ end
41
+
42
+ def check_required_files
43
+ # Just check if there are any files at all.
44
+ return if dependency_files.any?
45
+
46
+ raise "No Dockerfile!"
47
+ end
48
+
49
+ def updated_dockerfile_content(file)
50
+ updated_content =
51
+ if specified_with_digest?(file)
52
+ update_digest_and_tag(file)
53
+ else
54
+ update_tag(file)
55
+ end
56
+
57
+ raise "Expected content to change!" if updated_content == file.content
58
+
59
+ updated_content
60
+ end
61
+
62
+ def update_digest_and_tag(file)
63
+ old_declaration_regex = /^#{FROM_REGEX}\s+.*@#{old_digest(file)}/
64
+
65
+ file.content.gsub(old_declaration_regex) do |old_dec|
66
+ old_dec.
67
+ gsub("@#{old_digest(file)}", "@#{new_digest(file)}").
68
+ gsub(":#{dependency.previous_version}",
69
+ ":#{dependency.version}")
70
+ end
71
+ end
72
+
73
+ def update_tag(file)
74
+ return unless old_tag(file)
75
+
76
+ old_declaration =
77
+ if private_registry_url(file) then "#{private_registry_url(file)}/"
78
+ else ""
79
+ end
80
+ old_declaration += "#{dependency.name}:#{old_tag(file)}"
81
+ escaped_declaration = Regexp.escape(old_declaration)
82
+
83
+ old_declaration_regex = /^#{FROM_REGEX}\s+#{escaped_declaration}/
84
+
85
+ file.content.gsub(old_declaration_regex) do |old_dec|
86
+ old_dec.gsub(":#{old_tag(file)}", ":#{new_tag(file)}")
87
+ end
88
+ end
89
+
90
+ def specified_with_digest?(file)
91
+ dependency.
92
+ requirements.
93
+ find { |r| r[:file] == file.name }.
94
+ fetch(:source)[:digest]
95
+ end
96
+
97
+ def new_digest(file)
98
+ return unless specified_with_digest?(file)
99
+
100
+ dependency.requirements.
101
+ find { |r| r[:file] == file.name }.
102
+ fetch(:source).fetch(:digest)
103
+ end
104
+
105
+ def old_digest(file)
106
+ return unless specified_with_digest?(file)
107
+
108
+ dependency.previous_requirements.
109
+ find { |r| r[:file] == file.name }.
110
+ fetch(:source).fetch(:digest)
111
+ end
112
+
113
+ def new_tag(file)
114
+ dependency.requirements.
115
+ find { |r| r[:file] == file.name }.
116
+ fetch(:source)[:tag]
117
+ end
118
+
119
+ def old_tag(file)
120
+ dependency.previous_requirements.
121
+ find { |r| r[:file] == file.name }.
122
+ fetch(:source)[:tag]
123
+ end
124
+
125
+ def private_registry_url(file)
126
+ dependency.requirements.
127
+ find { |r| r[:file] == file.name }.
128
+ fetch(:source)[:registry]
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ Dependabot::FileUpdaters.
135
+ register("docker", Dependabot::Docker::FileUpdater)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/metadata_finders"
4
+ require "dependabot/metadata_finders/base"
5
+
6
+ module Dependabot
7
+ module Docker
8
+ class MetadataFinder < Dependabot::MetadataFinders::Base
9
+ private
10
+
11
+ def look_up_source
12
+ # TODO: Find a way to add links to PRs
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ Dependabot::MetadataFinders.
20
+ register("docker", Dependabot::Docker::MetadataFinder)
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "docker_registry2"
4
+
5
+ require "dependabot/update_checkers"
6
+ require "dependabot/update_checkers/base"
7
+ require "dependabot/errors"
8
+ require "dependabot/docker/utils/credentials_finder"
9
+
10
+ module Dependabot
11
+ module Docker
12
+ class UpdateChecker < Dependabot::UpdateCheckers::Base
13
+ VERSION_REGEX = /(?<version>[0-9]+(?:\.[a-zA-Z0-9]+)*)/.freeze
14
+ VERSION_WITH_SUFFIX =
15
+ /^#{VERSION_REGEX}(?<affix>-[a-z0-9.\-]+)?$/.freeze
16
+ VERSION_WITH_PREFIX =
17
+ /^(?<affix>[a-z0-9.\-]+-)?#{VERSION_REGEX}$/.freeze
18
+ NAME_WITH_VERSION =
19
+ /#{VERSION_WITH_PREFIX}|#{VERSION_WITH_SUFFIX}/.freeze
20
+
21
+ def latest_version
22
+ @latest_version ||= fetch_latest_version
23
+ end
24
+
25
+ def latest_resolvable_version
26
+ # Resolvability isn't an issue for Docker containers.
27
+ latest_version
28
+ end
29
+
30
+ def latest_resolvable_version_with_no_unlock
31
+ # No concept of "unlocking" for Docker containers
32
+ dependency.version
33
+ end
34
+
35
+ def updated_requirements
36
+ dependency.requirements.map do |req|
37
+ updated_source = req.fetch(:source).dup
38
+ updated_source[:digest] = updated_digest if req[:source][:digest]
39
+ updated_source[:tag] = latest_version if req[:source][:tag]
40
+
41
+ req.merge(source: updated_source)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def latest_version_resolvable_with_full_unlock?
48
+ # Full unlock checks aren't relevant for Dockerfiles
49
+ false
50
+ end
51
+
52
+ def updated_dependencies_after_full_unlock
53
+ raise NotImplementedError
54
+ end
55
+
56
+ def version_can_update?(*)
57
+ !version_up_to_date?
58
+ end
59
+
60
+ def version_up_to_date?
61
+ # If the tag isn't up-to-date then we can definitely update
62
+ return false if version_tag_up_to_date? == false
63
+
64
+ # Otherwise, if the Dockerfile specifies a digest check that that is
65
+ # up-to-date
66
+ digest_up_to_date?
67
+ end
68
+
69
+ def version_tag_up_to_date?
70
+ return unless dependency.version.match?(NAME_WITH_VERSION)
71
+
72
+ old_v = numeric_version_from(dependency.version)
73
+ latest_v = numeric_version_from(latest_version)
74
+
75
+ return true if version_class.new(latest_v) <= version_class.new(old_v)
76
+
77
+ # Check the precision of the potentially higher tag is the same as the
78
+ # one it would replace. In the event that it's not the same, check the
79
+ # digests are also unequal. Avoids 'updating' ruby-2 -> ruby-2.5.1
80
+ return false if old_v.split(".").count == latest_v.split(".").count
81
+
82
+ digest_of(dependency.version) == digest_of(latest_version)
83
+ end
84
+
85
+ def digest_up_to_date?
86
+ dependency.requirements.all? do |req|
87
+ next true unless req.fetch(:source)[:digest]
88
+
89
+ req.fetch(:source).fetch(:digest) == digest_of(dependency.version)
90
+ end
91
+ end
92
+
93
+ # Note: It's important that this *always* returns a version (even if
94
+ # it's the existing one) as it is what we later check the digest of.
95
+ def fetch_latest_version
96
+ unless dependency.version.match?(NAME_WITH_VERSION)
97
+ return dependency.version
98
+ end
99
+
100
+ # Prune out any downgrade tags before checking for pre-releases
101
+ # (which requires a call to the registry for each tag, so can be slow)
102
+ candidate_tags = comparable_tags_from_registry
103
+ non_downgrade_tags = remove_version_downgrades(candidate_tags)
104
+ candidate_tags = non_downgrade_tags if non_downgrade_tags.any?
105
+
106
+ wants_prerelease = prerelease?(dependency.version)
107
+ candidate_tags =
108
+ candidate_tags.
109
+ reject { |tag| prerelease?(tag) && !wants_prerelease }.
110
+ reject do |tag|
111
+ version = version_class.new(numeric_version_from(tag))
112
+ ignore_reqs.any? { |r| r.satisfied_by?(version) }
113
+ end
114
+
115
+ latest_tag =
116
+ candidate_tags.
117
+ max_by { |tag| version_class.new(numeric_version_from(tag)) }
118
+
119
+ latest_tag || dependency.version
120
+ end
121
+
122
+ def comparable_tags_from_registry
123
+ original_affix = affix_of(dependency.version)
124
+
125
+ tags_from_registry.
126
+ select { |tag| tag.match?(NAME_WITH_VERSION) }.
127
+ select { |tag| affix_of(tag) == original_affix }.
128
+ reject { |tag| commit_sha_suffix?(tag) }
129
+ end
130
+
131
+ def remove_version_downgrades(candidate_tags)
132
+ candidate_tags.select do |tag|
133
+ version_class.new(numeric_version_from(tag)) >=
134
+ version_class.new(numeric_version_from(dependency.version))
135
+ end
136
+ end
137
+
138
+ def commit_sha_suffix?(tag)
139
+ # Some people suffix their versions with commit SHAs. Dependabot
140
+ # can't order on those but will try to, so instead we should exclude
141
+ # them (unless there's a `latest` version pushed to the registry, in
142
+ # which case we'll use that to find the latest version)
143
+ return false unless tag.match?(/(^|\-)[0-9a-f]{7,}$/)
144
+
145
+ !tag.match?(/(^|\-)20[0-1]\d{5}$/)
146
+ end
147
+
148
+ def version_of_latest_tag
149
+ return unless latest_digest
150
+
151
+ tags_from_registry.
152
+ select { |tag| canonical_version?(tag) }.
153
+ select { |t| digest_of(t) == latest_digest }.
154
+ map { |t| version_class.new(numeric_version_from(t)) }.
155
+ max
156
+ end
157
+
158
+ def canonical_version?(tag)
159
+ return false unless numeric_version_from(tag)
160
+ return true if tag == numeric_version_from(tag)
161
+
162
+ # .NET tags are suffixed with -sdk. There may be other cases we need
163
+ # to consider in future, too.
164
+ tag == numeric_version_from(tag) + "-sdk"
165
+ end
166
+
167
+ def updated_digest
168
+ @updated_digest ||=
169
+ begin
170
+ docker_registry_client.digest(docker_repo_name, latest_version)
171
+ rescue RestClient::Exceptions::Timeout
172
+ attempt ||= 1
173
+ attempt += 1
174
+ raise if attempt > 3
175
+
176
+ retry
177
+ end
178
+ rescue DockerRegistry2::RegistryAuthenticationException,
179
+ RestClient::Forbidden
180
+ raise PrivateSourceAuthenticationFailure, registry_hostname
181
+ end
182
+
183
+ def tags_from_registry
184
+ @tags_from_registry ||=
185
+ begin
186
+ docker_registry_client.tags(docker_repo_name).fetch("tags")
187
+ rescue RestClient::Exceptions::Timeout
188
+ attempt ||= 1
189
+ attempt += 1
190
+ raise if attempt > 3
191
+
192
+ retry
193
+ end
194
+ rescue DockerRegistry2::RegistryAuthenticationException,
195
+ RestClient::Forbidden
196
+ raise PrivateSourceAuthenticationFailure, registry_hostname
197
+ end
198
+
199
+ def latest_digest
200
+ return unless tags_from_registry.include?("latest")
201
+
202
+ digest_of("latest")
203
+ end
204
+
205
+ def digest_of(tag)
206
+ @digests ||= {}
207
+ return @digests[tag] if @digests.key?(tag)
208
+
209
+ @digests[tag] =
210
+ begin
211
+ docker_registry_client.digest(docker_repo_name, tag)
212
+ rescue *transient_docker_errors => e
213
+ attempt ||= 1
214
+ attempt += 1
215
+ return if attempt > 3 && e.is_a?(DockerRegistry2::NotFound)
216
+ raise if attempt > 3
217
+
218
+ retry
219
+ end
220
+ end
221
+
222
+ def transient_docker_errors
223
+ [RestClient::Exceptions::Timeout, DockerRegistry2::NotFound]
224
+ end
225
+
226
+ def affix_of(tag)
227
+ tag.match(NAME_WITH_VERSION).named_captures.fetch("affix")
228
+ end
229
+
230
+ def prerelease?(tag)
231
+ return true if numeric_version_from(tag).match?(/[a-zA-Z]/)
232
+
233
+ # If we're dealing with a numeric version we can compare it against
234
+ # the digest for the `latest` tag.
235
+ return false unless numeric_version_from(tag)
236
+ return false unless latest_digest
237
+ return false unless version_of_latest_tag
238
+
239
+ version_class.new(numeric_version_from(tag)) > version_of_latest_tag
240
+ end
241
+
242
+ def numeric_version_from(tag)
243
+ return unless tag.match?(NAME_WITH_VERSION)
244
+
245
+ tag.match(NAME_WITH_VERSION).named_captures.fetch("version")
246
+ end
247
+
248
+ def registry_hostname
249
+ dependency.requirements.first[:source][:registry] ||
250
+ "registry.hub.docker.com"
251
+ end
252
+
253
+ def using_dockerhub?
254
+ registry_hostname == "registry.hub.docker.com"
255
+ end
256
+
257
+ def registry_credentials
258
+ credentials_finder.credentials_for_registry(registry_hostname)
259
+ end
260
+
261
+ def credentials_finder
262
+ @credentials_finder ||= Utils::CredentialsFinder.new(credentials)
263
+ end
264
+
265
+ def docker_repo_name
266
+ return dependency.name unless using_dockerhub?
267
+ return dependency.name unless dependency.name.split("/").count < 2
268
+
269
+ "library/#{dependency.name}"
270
+ end
271
+
272
+ def docker_registry_client
273
+ @docker_registry_client ||=
274
+ DockerRegistry2::Registry.new(
275
+ "https://#{registry_hostname}",
276
+ user: registry_credentials&.fetch("username"),
277
+ password: registry_credentials&.fetch("password")
278
+ )
279
+ end
280
+
281
+ def ignore_reqs
282
+ # Note: we use Gem::Requirement here because ignore conditions will
283
+ # be passed as Ruby ranges
284
+ ignored_versions.map { |req| Gem::Requirement.new(req.split(",")) }
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ Dependabot::UpdateCheckers.
291
+ register("docker", Dependabot::Docker::UpdateChecker)
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-ecr"
4
+ require "base64"
5
+
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Docker
10
+ module Utils
11
+ class CredentialsFinder
12
+ AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+).amazonaws\.com/.freeze
13
+
14
+ def initialize(credentials)
15
+ @credentials = credentials
16
+ end
17
+
18
+ def credentials_for_registry(registry_hostname)
19
+ registry_details =
20
+ credentials.
21
+ select { |cred| cred["type"] == "docker_registry" }.
22
+ find { |cred| cred.fetch("registry") == registry_hostname }
23
+ return unless registry_details
24
+ return registry_details unless registry_hostname.match?(AWS_ECR_URL)
25
+
26
+ build_aws_credentials(registry_details)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :credentials
32
+
33
+ def build_aws_credentials(registry_details)
34
+ # If credentials have been generated from AWS we can just return them
35
+ return registry_details if registry_details.fetch("username") == "AWS"
36
+
37
+ # Otherwise, we need to use the provided Access Key ID and secret to
38
+ # generate a temporary username and password
39
+ aws_credentials = Aws::Credentials.new(
40
+ registry_details.fetch("username"),
41
+ registry_details.fetch("password")
42
+ )
43
+
44
+ registry_hostname = registry_details.fetch("registry")
45
+ region = registry_hostname.match(AWS_ECR_URL).
46
+ named_captures.fetch("region")
47
+
48
+ @authorization_tokens ||= {}
49
+ @authorization_tokens[registry_hostname] ||=
50
+ Aws::ECR::Client.new(region: region, credentials: aws_credentials).
51
+ get_authorization_token.authorization_data.first.
52
+ authorization_token
53
+
54
+ username, password =
55
+ Base64.decode64(@authorization_tokens[registry_hostname]).split(":")
56
+
57
+ registry_details.merge("username" => username, "password" => password)
58
+ rescue Aws::Errors::MissingCredentialsError,
59
+ Aws::ECR::Errors::UnrecognizedClientException
60
+ raise PrivateSourceAuthenticationFailure, registry_hostname
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dependabot-docker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.77.0
5
+ platform: ruby
6
+ authors:
7
+ - Dependabot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dependabot-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.77.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.77.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec_junit_formatter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.61'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.61'
111
+ - !ruby/object:Gem::Dependency
112
+ name: vcr
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.4'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.4'
139
+ description: Automated dependency management for Ruby, JavaScript, Python, PHP, Elixir,
140
+ Rust, Java, .NET, Elm and Go
141
+ email: support@dependabot.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - lib/dependabot/docker/file_fetcher.rb
147
+ - lib/dependabot/docker/file_parser.rb
148
+ - lib/dependabot/docker/file_updater.rb
149
+ - lib/dependabot/docker/metadata_finder.rb
150
+ - lib/dependabot/docker/update_checker.rb
151
+ - lib/dependabot/docker/utils/credentials_finder.rb
152
+ homepage: https://github.com/dependabot/dependabot-core
153
+ licenses:
154
+ - Nonstandard
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: 2.5.0
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: 2.5.0
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.7.6
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Terraform support for dependabot-core
176
+ test_files: []