dependabot-docker 0.206.0 → 0.209.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dependabot/docker/file_fetcher.rb +51 -4
- data/lib/dependabot/docker/file_parser.rb +74 -3
- data/lib/dependabot/docker/file_updater.rb +58 -6
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00f4377178bcfeb2dab9cd43cfc073184f3889bfc58c6775e43cb4af74d6d51a
|
4
|
+
data.tar.gz: 4688f5144e93f55a744a5c78b0f27b0c976147ac0f947b7898525dbdee4dc77a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08567ef1cd81d990857e8f028ec982fdb263469a3eab619cc84cdc4deb6d7bed1d5d85135d9585f88871404279bf6be47571b7896a92c5e32952f99df0aee821'
|
7
|
+
data.tar.gz: 04b846444b46050a0a899f7a510a07b7be67e97e60e3fbea3d5d822940a45b9301f86bd0a37d36e4a4e97d369acd8ab92e9a25ba4263facd2b322db4dd4bf9af
|
@@ -6,27 +6,46 @@ require "dependabot/file_fetchers/base"
|
|
6
6
|
module Dependabot
|
7
7
|
module Docker
|
8
8
|
class FileFetcher < Dependabot::FileFetchers::Base
|
9
|
+
YAML_REGEXP = /^[^\.]+\.ya?ml$/i.freeze
|
10
|
+
DOCKER_REGEXP = /dockerfile/i.freeze
|
11
|
+
|
9
12
|
def self.required_files_in?(filenames)
|
10
|
-
filenames.any? { |f| f.match?(
|
13
|
+
filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
|
14
|
+
filenames.any? { |f| f.match?(YAML_REGEXP) }
|
11
15
|
end
|
12
16
|
|
13
17
|
def self.required_files_message
|
14
|
-
"Repo must contain a Dockerfile."
|
18
|
+
"Repo must contain a Dockerfile or Kubernetes YAML files."
|
15
19
|
end
|
16
20
|
|
17
21
|
private
|
18
22
|
|
23
|
+
def kubernetes_enabled?
|
24
|
+
options.key?(:kubernetes_updates) && options[:kubernetes_updates]
|
25
|
+
end
|
26
|
+
|
19
27
|
def fetch_files
|
20
28
|
fetched_files = []
|
21
29
|
fetched_files += correctly_encoded_dockerfiles
|
30
|
+
fetched_files += correctly_encoded_yamlfiles if kubernetes_enabled?
|
22
31
|
|
23
32
|
return fetched_files if fetched_files.any?
|
24
33
|
|
25
|
-
if incorrectly_encoded_dockerfiles.none?
|
34
|
+
if !kubernetes_enabled? && incorrectly_encoded_dockerfiles.none?
|
26
35
|
raise(
|
27
36
|
Dependabot::DependencyFileNotFound,
|
28
37
|
File.join(directory, "Dockerfile")
|
29
38
|
)
|
39
|
+
elsif incorrectly_encoded_dockerfiles.none? && incorrectly_encoded_yamlfiles.none?
|
40
|
+
raise(
|
41
|
+
Dependabot::DependabotError,
|
42
|
+
"Found neither Kubernetes YAML nor Dockerfiles in #{directory}"
|
43
|
+
)
|
44
|
+
elsif incorrectly_encoded_dockerfiles.none?
|
45
|
+
raise(
|
46
|
+
Dependabot::DependencyFileNotParseable,
|
47
|
+
incorrectly_encoded_yamlfiles.first.path
|
48
|
+
)
|
30
49
|
else
|
31
50
|
raise(
|
32
51
|
Dependabot::DependencyFileNotParseable,
|
@@ -38,7 +57,7 @@ module Dependabot
|
|
38
57
|
def dockerfiles
|
39
58
|
@dockerfiles ||=
|
40
59
|
repo_contents(raise_errors: false).
|
41
|
-
select { |f| f.type == "file" && f.name.match?(
|
60
|
+
select { |f| f.type == "file" && f.name.match?(DOCKER_REGEXP) }.
|
42
61
|
map { |f| fetch_file_from_host(f.name) }
|
43
62
|
end
|
44
63
|
|
@@ -49,6 +68,34 @@ module Dependabot
|
|
49
68
|
def incorrectly_encoded_dockerfiles
|
50
69
|
dockerfiles.reject { |f| f.content.valid_encoding? }
|
51
70
|
end
|
71
|
+
|
72
|
+
def yamlfiles
|
73
|
+
@yamlfiles ||=
|
74
|
+
repo_contents(raise_errors: false).
|
75
|
+
select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }.
|
76
|
+
map { |f| fetch_file_from_host(f.name) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def likely_kubernetes_resource?(resource)
|
80
|
+
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
81
|
+
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
82
|
+
end
|
83
|
+
|
84
|
+
def correctly_encoded_yamlfiles
|
85
|
+
candidate_files = yamlfiles.select { |f| f.content.valid_encoding? }
|
86
|
+
candidate_files.select do |f|
|
87
|
+
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
88
|
+
# in a multi-resource file had better be a valid k8s resource
|
89
|
+
content = ::YAML.safe_load(f.content, aliases: true)
|
90
|
+
likely_kubernetes_resource?(content)
|
91
|
+
rescue ::Psych::Exception
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def incorrectly_encoded_yamlfiles
|
97
|
+
yamlfiles.reject { |f| f.content.valid_encoding? }
|
98
|
+
end
|
52
99
|
end
|
53
100
|
end
|
54
101
|
end
|
@@ -34,6 +34,9 @@ module Dependabot
|
|
34
34
|
|
35
35
|
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/.freeze
|
36
36
|
|
37
|
+
IMAGE_SPEC =
|
38
|
+
%r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x.freeze
|
39
|
+
|
37
40
|
def parse
|
38
41
|
dependency_set = DependencySet.new
|
39
42
|
|
@@ -61,15 +64,18 @@ module Dependabot
|
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
67
|
+
manifest_files.each do |file|
|
68
|
+
dependency_set += workfile_file_dependencies(file)
|
69
|
+
end
|
70
|
+
|
64
71
|
dependency_set.dependencies
|
65
72
|
end
|
66
73
|
|
67
74
|
private
|
68
75
|
|
69
76
|
def dockerfiles
|
70
|
-
# The Docker file fetcher
|
71
|
-
|
72
|
-
dependency_files
|
77
|
+
# The Docker file fetcher fetches Dockerfiles and yaml files. Reject yaml files.
|
78
|
+
dependency_files.reject { |f| f.type == "file" && f.name.match?(/^[^\.]+\.ya?ml/i) }
|
73
79
|
end
|
74
80
|
|
75
81
|
def version_from(parsed_from_line)
|
@@ -154,6 +160,71 @@ module Dependabot
|
|
154
160
|
|
155
161
|
raise "No Dockerfile!"
|
156
162
|
end
|
163
|
+
|
164
|
+
def workfile_file_dependencies(file)
|
165
|
+
dependency_set = DependencySet.new
|
166
|
+
|
167
|
+
resources = file.content.split(/^---$/).map(&:strip).reject(&:empty?) # assuming a yaml file
|
168
|
+
resources.flat_map do |resource|
|
169
|
+
json = YAML.safe_load(resource, aliases: true)
|
170
|
+
images = deep_fetch_images(json).uniq
|
171
|
+
|
172
|
+
images.each do |string|
|
173
|
+
# TODO: Support Docker references and path references
|
174
|
+
details = string.match(IMAGE_SPEC).named_captures
|
175
|
+
details["registry"] = nil if details["registry"] == "docker.io"
|
176
|
+
|
177
|
+
version = version_from(details)
|
178
|
+
next unless version
|
179
|
+
|
180
|
+
dependency_set << build_image_dependency(file, details, version)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
dependency_set
|
185
|
+
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
|
186
|
+
raise Dependabot::DependencyFileNotParseable, file.path
|
187
|
+
end
|
188
|
+
|
189
|
+
def build_image_dependency(file, details, version)
|
190
|
+
Dependency.new(
|
191
|
+
name: details.fetch("image"),
|
192
|
+
version: version,
|
193
|
+
package_manager: "docker",
|
194
|
+
requirements: [
|
195
|
+
requirement: nil,
|
196
|
+
groups: [],
|
197
|
+
file: file.name,
|
198
|
+
source: source_from(details)
|
199
|
+
]
|
200
|
+
)
|
201
|
+
end
|
202
|
+
|
203
|
+
def deep_fetch_images(json_obj)
|
204
|
+
case json_obj
|
205
|
+
when Hash then deep_fetch_images_from_hash(json_obj)
|
206
|
+
when Array then json_obj.flat_map { |o| deep_fetch_images(o) }
|
207
|
+
else []
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def deep_fetch_images_from_hash(json_object)
|
212
|
+
img = json_object.fetch("image", nil)
|
213
|
+
|
214
|
+
images =
|
215
|
+
if !img.nil? && img.is_a?(String) && !img.empty?
|
216
|
+
[img]
|
217
|
+
else
|
218
|
+
[]
|
219
|
+
end
|
220
|
+
|
221
|
+
images + json_object.values.flat_map { |obj| deep_fetch_images(obj) }
|
222
|
+
end
|
223
|
+
|
224
|
+
def manifest_files
|
225
|
+
# Dependencies include both Dockerfiles and yaml, select yaml.
|
226
|
+
dependency_files.select { |f| f.type == "file" && f.name.match?(/^[^\.]+\.ya?ml/i) }
|
227
|
+
end
|
157
228
|
end
|
158
229
|
end
|
159
230
|
end
|
@@ -10,7 +10,10 @@ module Dependabot
|
|
10
10
|
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i.freeze
|
11
11
|
|
12
12
|
def self.updated_files_regex
|
13
|
-
[
|
13
|
+
[
|
14
|
+
/dockerfile/i,
|
15
|
+
/^[^\.]+\.ya?ml/i
|
16
|
+
]
|
14
17
|
end
|
15
18
|
|
16
19
|
def updated_dependency_files
|
@@ -19,11 +22,17 @@ module Dependabot
|
|
19
22
|
dependency_files.each do |file|
|
20
23
|
next unless requirement_changed?(file, dependency)
|
21
24
|
|
22
|
-
updated_files <<
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
updated_files << if file.name.match?(/^[^\.]+\.ya?ml/i)
|
26
|
+
updated_file(
|
27
|
+
file: file,
|
28
|
+
content: updated_yaml_content(file)
|
29
|
+
)
|
30
|
+
else
|
31
|
+
updated_file(
|
32
|
+
file: file,
|
33
|
+
content: updated_dockerfile_content(file)
|
34
|
+
)
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
updated_files.reject! { |f| dependency_files.include?(f) }
|
@@ -138,6 +147,49 @@ module Dependabot
|
|
138
147
|
find { |r| r[:file] == file.name }.
|
139
148
|
fetch(:source)[:registry]
|
140
149
|
end
|
150
|
+
|
151
|
+
def updated_yaml_content(file)
|
152
|
+
updated_content = update_image(file)
|
153
|
+
|
154
|
+
raise "Expected content to change!" if updated_content == file.content
|
155
|
+
|
156
|
+
updated_content
|
157
|
+
end
|
158
|
+
|
159
|
+
def update_image(file)
|
160
|
+
old_images = old_yaml_images(file)
|
161
|
+
return if old_images.empty?
|
162
|
+
|
163
|
+
modified_content = file.content
|
164
|
+
|
165
|
+
old_images.each do |old_image|
|
166
|
+
old_image_regex = /^\s+image:\s+#{old_image}(?=\s|$)/
|
167
|
+
modified_content = modified_content.gsub(old_image_regex) do |old_img|
|
168
|
+
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
modified_content
|
173
|
+
end
|
174
|
+
|
175
|
+
def new_yaml_image(file)
|
176
|
+
elt = dependency.requirements.find { |r| r[:file] == file.name }
|
177
|
+
prefix = elt.fetch(:source)[:registry] ? "#{elt.fetch(:source)[:registry]}/" : ""
|
178
|
+
digest = elt.fetch(:source)[:digest] ? "@#{elt.fetch(:source)[:digest]}" : ""
|
179
|
+
tag = elt.fetch(:source)[:tag] ? ":#{elt.fetch(:source)[:tag]}" : ""
|
180
|
+
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def old_yaml_images(file)
|
184
|
+
dependency.
|
185
|
+
previous_requirements.
|
186
|
+
select { |r| r[:file] == file.name }.map do |r|
|
187
|
+
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
188
|
+
digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : ""
|
189
|
+
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
190
|
+
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
191
|
+
end
|
192
|
+
end
|
141
193
|
end
|
142
194
|
end
|
143
195
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-docker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.209.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.209.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.209.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debase
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|