dependabot-docker 0.208.0 → 0.209.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 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