dependabot-docker_compose 0.298.0 → 0.299.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.
@@ -1,80 +0,0 @@
1
- # typed: strong
2
- # frozen_string_literal: true
3
-
4
- require "dependabot/dependency"
5
- require "dependabot/file_parsers"
6
- require "dependabot/file_parsers/base"
7
- require "sorbet-runtime"
8
-
9
- module Dependabot
10
- module Shared
11
- class SharedFileParser < Dependabot::FileParsers::Base
12
- extend T::Sig
13
- extend T::Helpers
14
-
15
- abstract!
16
-
17
- require "dependabot/file_parsers/base/dependency_set"
18
-
19
- # Details of Docker regular expressions is at
20
- # https://github.com/docker/distribution/blob/master/reference/regexp.go
21
- DOMAIN_COMPONENT = /(?:[[:alnum:]]|[[:alnum:]][[[:alnum:]]-]*[[:alnum:]])/
22
- DOMAIN = /(?:#{DOMAIN_COMPONENT}(?:\.#{DOMAIN_COMPONENT})+)/
23
- REGISTRY = /(?<registry>#{DOMAIN}(?::\d+)?)/
24
-
25
- NAME_COMPONENT = /(?:[a-z\d]+(?:(?:[._]|__|[-]*)[a-z\d]+)*)/
26
- IMAGE = %r{(?<image>#{NAME_COMPONENT}(?:/#{NAME_COMPONENT})*)}
27
-
28
- TAG = /:(?<tag>[\w][\w.-]{0,127})/
29
- DIGEST = /@(?<digest>[^\s]+)/
30
- NAME = /\s+AS\s+(?<name>[\w-]+)/
31
-
32
- protected
33
-
34
- sig { params(parsed_line: T::Hash[String, T.nilable(String)]).returns(T.nilable(String)) }
35
- def version_from(parsed_line)
36
- parsed_line.fetch("tag") || parsed_line.fetch("digest")
37
- end
38
-
39
- sig { params(parsed_line: T::Hash[String, T.nilable(String)]).returns(T::Hash[String, T.nilable(String)]) }
40
- def source_from(parsed_line)
41
- source = {}
42
-
43
- source[:registry] = parsed_line.fetch("registry") if parsed_line.fetch("registry")
44
- source[:tag] = parsed_line.fetch("tag") if parsed_line.fetch("tag")
45
- source[:digest] = parsed_line.fetch("digest") if parsed_line.fetch("digest")
46
-
47
- source
48
- end
49
-
50
- sig do
51
- params(file: Dependabot::DependencyFile, details: T::Hash[String, T.nilable(String)],
52
- version: String).returns(Dependabot::Dependency)
53
- end
54
- def build_dependency(file, details, version)
55
- Dependency.new(
56
- name: T.must(details.fetch("image")),
57
- version: version,
58
- package_manager: package_manager,
59
- requirements: [
60
- requirement: nil,
61
- groups: [],
62
- file: file.name,
63
- source: source_from(details)
64
- ]
65
- )
66
- end
67
-
68
- private
69
-
70
- sig { override.void }
71
- def check_required_files; end
72
-
73
- sig { abstract.returns(String) }
74
- def package_manager; end
75
-
76
- sig { abstract.returns(String) }
77
- def file_type; end
78
- end
79
- end
80
- end
@@ -1,261 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "dependabot/file_updaters"
5
- require "dependabot/file_updaters/base"
6
- require "dependabot/errors"
7
- require "sorbet-runtime"
8
- require "dependabot/shared/utils/helpers"
9
-
10
- module Dependabot
11
- module Shared
12
- class SharedFileUpdater < Dependabot::FileUpdaters::Base
13
- extend T::Sig
14
- extend T::Helpers
15
-
16
- abstract!
17
-
18
- FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
19
-
20
- sig { override.returns(T::Array[Dependabot::DependencyFile]) }
21
- def updated_dependency_files
22
- updated_files = []
23
- dependency_files.each do |file|
24
- next unless requirement_changed?(file, T.must(dependency))
25
-
26
- updated_files << if file.name.match?(T.must(yaml_file_pattern))
27
- updated_file(
28
- file: file,
29
- content: T.must(updated_yaml_content(file))
30
- )
31
- else
32
- updated_file(
33
- file: file,
34
- content: T.must(updated_dockerfile_content(file))
35
- )
36
- end
37
- end
38
-
39
- updated_files.reject! { |f| dependency_files.include?(f) }
40
- raise "No files changed!" if updated_files.none?
41
-
42
- updated_files
43
- end
44
-
45
- sig { abstract.returns(T.nilable(Regexp)) }
46
- def yaml_file_pattern; end
47
-
48
- sig { abstract.returns(T.nilable(Regexp)) }
49
- def container_image_regex; end
50
-
51
- private
52
-
53
- sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
54
- def updated_dockerfile_content(file)
55
- old_sources = previous_sources(file)
56
- new_sources = sources(file)
57
-
58
- updated_content = T.let(file.content, T.untyped)
59
-
60
- T.must(old_sources).zip(new_sources).each do |old_source, new_source|
61
- updated_content = update_digest_and_tag(updated_content, old_source, T.must(new_source))
62
- end
63
-
64
- raise "Expected content to change!" if updated_content == file.content
65
-
66
- updated_content
67
- end
68
-
69
- sig do
70
- params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
71
- new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
72
- end
73
- def update_digest_and_tag(previous_content, old_source, new_source)
74
- old_digest = old_source[:digest]
75
- new_digest = new_source[:digest]
76
-
77
- old_tag = old_source[:tag]
78
- new_tag = new_source[:tag]
79
-
80
- old_declaration =
81
- if private_registry_url(old_source)
82
- "#{private_registry_url(old_source)}/"
83
- else
84
- ""
85
- end
86
- old_declaration += T.must(dependency).name
87
- old_declaration +=
88
- if specified_with_tag?(old_source)
89
- ":#{old_tag}"
90
- else
91
- ""
92
- end
93
- old_declaration +=
94
- if specified_with_digest?(old_source)
95
- "@sha256:#{old_digest}"
96
- else
97
- ""
98
- end
99
-
100
- escaped_declaration = Regexp.escape(old_declaration)
101
-
102
- old_declaration_regex = build_old_declaration_regex(escaped_declaration)
103
-
104
- previous_content.gsub(old_declaration_regex) do |old_dec|
105
- old_dec
106
- .gsub("@sha256:#{old_digest}", "@sha256:#{new_digest}")
107
- .gsub(":#{old_tag}", ":#{new_tag}")
108
- end
109
- end
110
-
111
- sig { params(escaped_declaration: String).returns(Regexp) }
112
- def build_old_declaration_regex(escaped_declaration)
113
- %r{^#{FROM_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
114
- end
115
-
116
- sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
117
- def updated_yaml_content(file)
118
- updated_content = file.content
119
- updated_content = update_helm(file, updated_content) if Shared::Utils.likely_helm_chart?(file)
120
- updated_content = update_image(file, updated_content)
121
-
122
- raise "Expected content to change!" if updated_content == file.content
123
-
124
- updated_content
125
- end
126
-
127
- sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
128
- def update_helm(file, content)
129
- old_tags = old_helm_tags(file)
130
- return if old_tags.empty?
131
-
132
- modified_content = content
133
-
134
- old_tags.each do |old_tag|
135
- old_tag_regex = /^\s*(?:-\s)?(?:tag|version):\s+["']?#{old_tag}["']?(?=\s|$)/
136
- modified_content = modified_content&.gsub(old_tag_regex) do |old_img_tag|
137
- old_img_tag.gsub(old_tag.to_s, new_helm_tag(file).to_s)
138
- end
139
- end
140
- modified_content
141
- end
142
-
143
- sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
144
- def update_image(file, content)
145
- old_images = old_yaml_images(file)
146
- return if old_images.empty?
147
-
148
- modified_content = content
149
-
150
- old_images.each do |old_image|
151
- old_image_regex = /^\s*(?:-\s)?image:\s+#{old_image}(?=\s|$)/
152
- modified_content = modified_content&.gsub(old_image_regex) do |old_img|
153
- old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
154
- end
155
- end
156
- modified_content
157
- end
158
-
159
- sig { params(file: Dependabot::DependencyFile).returns(String) }
160
- def new_yaml_image(file)
161
- element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
162
- prefix = element&.dig(:source, :registry) ? "#{element.fetch(:source)[:registry]}/" : ""
163
- digest = element&.dig(:source, :digest) ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
164
- tag = element&.dig(:source, :tag) ? ":#{element.fetch(:source)[:tag]}" : ""
165
- "#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
166
- end
167
-
168
- sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
169
- def old_yaml_images(file)
170
- T.must(previous_requirements(file)).map do |r|
171
- prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
172
- digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
173
- tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
174
- "#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
175
- end
176
- end
177
-
178
- sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
179
- def old_helm_tags(file)
180
- T.must(previous_requirements(file)).map do |r|
181
- tag = r.fetch(:source)[:tag] || ""
182
- digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
183
- "#{tag}#{digest}"
184
- end
185
- end
186
-
187
- sig { params(file: Dependabot::DependencyFile).returns(String) }
188
- def new_helm_tag(file)
189
- element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
190
- tag = T.must(element).dig(:source, :tag) || ""
191
- digest = T.must(element).dig(:source, :digest) ? "@sha256:#{T.must(element).dig(:source, :digest)}" : ""
192
- "#{tag}#{digest}"
193
- end
194
-
195
- protected
196
-
197
- sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
198
- def requirement_changed?(file, dependency)
199
- changed_requirements =
200
- dependency.requirements - T.must(dependency.previous_requirements)
201
-
202
- changed_requirements.any? { |f| f[:file] == file.name }
203
- end
204
-
205
- sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) }
206
- def specified_with_tag?(source)
207
- !source[:tag].nil?
208
- end
209
-
210
- sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) }
211
- def specified_with_digest?(source)
212
- !source[:digest].nil?
213
- end
214
-
215
- sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
216
- def requirements(file)
217
- T.must(dependency).requirements
218
- .select { |r| r[:file] == file.name }
219
- end
220
-
221
- sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
222
- def previous_requirements(file)
223
- T.must(dependency).previous_requirements
224
- &.select { |r| r[:file] == file.name }
225
- end
226
-
227
- sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
228
- def private_registry_url(source)
229
- source[:registry]
230
- end
231
-
232
- sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.nilable(String)]]) }
233
- def sources(file)
234
- requirements(file).map { |r| r.fetch(:source) }
235
- end
236
-
237
- sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.nilable(String)]])) }
238
- def previous_sources(file)
239
- previous_requirements(file)&.map { |r| r.fetch(:source) }
240
- end
241
-
242
- sig { returns(T.nilable(Dependabot::Dependency)) }
243
- def dependency
244
- # Files will only ever be updating a single dependency
245
- dependencies.first
246
- end
247
-
248
- sig { override.void }
249
- def check_required_files
250
- return if dependency_files.any?
251
-
252
- raise "No #{file_type}!"
253
- end
254
-
255
- private
256
-
257
- sig { abstract.returns(String) }
258
- def file_type; end
259
- end
260
- end
261
- end
@@ -1,101 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "aws-sdk-ecr"
5
- require "base64"
6
- require "sorbet-runtime"
7
-
8
- require "dependabot/credential"
9
- require "dependabot/errors"
10
-
11
- module Dependabot
12
- module Shared
13
- module Utils
14
- class CredentialsFinder
15
- extend T::Sig
16
-
17
- AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
18
- DEFAULT_DOCKER_HUB_REGISTRY = "registry.hub.docker.com"
19
-
20
- sig { params(credentials: T::Array[Dependabot::Credential]).void }
21
- def initialize(credentials)
22
- @credentials = credentials
23
- end
24
-
25
- sig { params(registry_hostname: T.nilable(String)).returns(T.nilable(Dependabot::Credential)) }
26
- def credentials_for_registry(registry_hostname)
27
- registry_details =
28
- credentials
29
- .select { |cred| cred["type"] == "docker_registry" }
30
- .find { |cred| cred.fetch("registry") == registry_hostname }
31
- return unless registry_details
32
- return registry_details unless registry_hostname&.match?(AWS_ECR_URL)
33
-
34
- build_aws_credentials(registry_details)
35
- end
36
-
37
- sig { returns(T.nilable(String)) }
38
- def base_registry
39
- @base_registry ||= T.let(
40
- credentials.find do |cred|
41
- cred["type"] == "docker_registry" && cred.replaces_base?
42
- end,
43
- T.nilable(Dependabot::Credential)
44
- )
45
- @base_registry ||= Dependabot::Credential.new({ "registry" => DEFAULT_DOCKER_HUB_REGISTRY,
46
- "credentials" => nil })
47
- @base_registry["registry"]
48
- end
49
-
50
- sig { params(registry: String).returns(T::Boolean) }
51
- def using_dockerhub?(registry)
52
- registry == DEFAULT_DOCKER_HUB_REGISTRY
53
- end
54
-
55
- private
56
-
57
- sig { returns(T::Array[Dependabot::Credential]) }
58
- attr_reader :credentials
59
-
60
- sig { params(registry_details: Dependabot::Credential).returns(Dependabot::Credential) }
61
- def build_aws_credentials(registry_details)
62
- # If credentials have been generated from AWS we can just return them
63
- return registry_details if registry_details["username"] == "AWS"
64
-
65
- # Build a client either with explicit creds or default creds
66
- registry_hostname = registry_details.fetch("registry")
67
- region = registry_hostname.match(AWS_ECR_URL).named_captures.fetch("region")
68
- aws_credentials = Aws::Credentials.new(
69
- registry_details["username"],
70
- registry_details["password"]
71
- )
72
-
73
- ecr_client =
74
- if aws_credentials.set?
75
- Aws::ECR::Client.new(region: region, credentials: aws_credentials)
76
- else
77
- # Let the client check default locations for credentials
78
- Aws::ECR::Client.new(region: region)
79
- end
80
-
81
- # If the client still lacks credentials, we might be running within GitHub's
82
- # Dependabot Service, in which case we might get them from the proxy
83
- return registry_details if ecr_client.config.credentials.nil?
84
-
85
- # Otherwise, we need to use the provided Access Key ID and secret to
86
- # generate a temporary username and password
87
- @authorization_tokens ||= T.let({}, T.nilable(T::Hash[String, String]))
88
- @authorization_tokens[registry_hostname] ||=
89
- ecr_client.get_authorization_token.authorization_data.first.authorization_token
90
- username, password =
91
- Base64.decode64(T.must(@authorization_tokens[registry_hostname])).split(":")
92
- registry_details.merge(Dependabot::Credential.new({ "username" => username, "password" => password }))
93
- rescue Aws::Errors::MissingCredentialsError,
94
- Aws::ECR::Errors::UnrecognizedClientException,
95
- Aws::ECR::Errors::InvalidSignatureException
96
- raise PrivateSourceAuthenticationFailure, registry_hostname
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,19 +0,0 @@
1
- # typed: strong
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- module Dependabot
7
- module Shared
8
- module Utils
9
- HELM_REGEXP = /values[\-a-zA-Z_0-9]*\.ya?ml$/i
10
-
11
- extend T::Sig
12
-
13
- sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
14
- def self.likely_helm_chart?(file)
15
- file.name.match?(HELM_REGEXP)
16
- end
17
- end
18
- end
19
- end