dependabot-docker 0.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []