dependabot-docker 0.208.0 → 0.209.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c6ea326363b1f3e9c5f5adb5d511b47f3fb6c8b863c9f49b8a87d8a1f8b346c
4
- data.tar.gz: 138ffefc8d307fa05faf9668ef7abc32029c2d752957429bfdf71c4e1d40932d
3
+ metadata.gz: 00f4377178bcfeb2dab9cd43cfc073184f3889bfc58c6775e43cb4af74d6d51a
4
+ data.tar.gz: 4688f5144e93f55a744a5c78b0f27b0c976147ac0f947b7898525dbdee4dc77a
5
5
  SHA512:
6
- metadata.gz: 36ec21f80adb2c26514407aa8377cb0dfd98941514a88077bed177fae7b87d326352d5c8a5702de4a12c2d4062c71074aa297c15c09272856d137d8ddac13d03
7
- data.tar.gz: 57fa148d7b13064b4367ca8dee1fab920f12c4c5d3ee5936ecb6378cd2eb4b25870392911439256b835ff0d7118a07f967a795df8d1923f7b207f809357d7369
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?(/dockerfile/i) }
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?(/dockerfile/i) }.
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 only fetches Dockerfiles, so no need to
71
- # filter here
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
- [/dockerfile/i]
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
- updated_file(
24
- file: file,
25
- content: updated_dockerfile_content(file)
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.208.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-16 00:00:00.000000000 Z
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.208.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.208.0
26
+ version: 0.209.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debase
29
29
  requirement: !ruby/object:Gem::Requirement