dependabot-docker 0.208.0 → 0.211.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: 6c194c165762186cd5b9ca1048491778c1a49b1ea11d19a23c44e7eb501e6231
4
+ data.tar.gz: '09532e3bed9e17abe29e4c1667d1918025f081973af0eef0178477f3cd7a6919'
5
5
  SHA512:
6
- metadata.gz: 36ec21f80adb2c26514407aa8377cb0dfd98941514a88077bed177fae7b87d326352d5c8a5702de4a12c2d4062c71074aa297c15c09272856d137d8ddac13d03
7
- data.tar.gz: 57fa148d7b13064b4367ca8dee1fab920f12c4c5d3ee5936ecb6378cd2eb4b25870392911439256b835ff0d7118a07f967a795df8d1923f7b207f809357d7369
6
+ metadata.gz: e7108f82ff87b6962c1f1ec837da3078cac2c0ee1d3dddcaf0956256c5ceaf26846be9e0e7674346cdcaf19f681fa0bfd04f15d26a803b2a58c54a826f4694f0
7
+ data.tar.gz: 639d3a059228017338711f8292a7d2a8154b9cf9b857d2177a12ceaf0fd00f57045d3a870652b8e638fbd8e73dd2436cb6c6e5cb77a0ecdbda99c761707785a7
@@ -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) }
@@ -76,7 +85,11 @@ module Dependabot
76
85
 
77
86
  modified_content = file.content
78
87
 
79
- old_tags.each do |old_tag|
88
+ new_tags = new_tags(file)
89
+
90
+ old_tags.zip(new_tags).each do |old_tag, new_tag|
91
+ new_tag ||= new_tags.first
92
+
80
93
  old_declaration =
81
94
  if private_registry_url(file) then "#{private_registry_url(file)}/"
82
95
  else
@@ -90,7 +103,7 @@ module Dependabot
90
103
 
91
104
  modified_content = modified_content.
92
105
  gsub(old_declaration_regex) do |old_dec|
93
- old_dec.gsub(":#{old_tag}", ":#{new_tag(file)}")
106
+ old_dec.gsub(":#{old_tag}", ":#{new_tag}")
94
107
  end
95
108
  end
96
109
 
@@ -120,10 +133,10 @@ module Dependabot
120
133
  fetch(:source).fetch(:digest)
121
134
  end
122
135
 
123
- def new_tag(file)
136
+ def new_tags(file)
124
137
  dependency.requirements.
125
- find { |r| r[:file] == file.name }.
126
- fetch(:source)[:tag]
138
+ select { |r| r[:file] == file.name }.
139
+ map { |r| r.fetch(:source)[:tag] }
127
140
  end
128
141
 
129
142
  def old_tags(file)
@@ -138,6 +151,48 @@ module Dependabot
138
151
  find { |r| r[:file] == file.name }.
139
152
  fetch(:source)[:registry]
140
153
  end
154
+
155
+ def updated_yaml_content(file)
156
+ updated_content = update_image(file)
157
+
158
+ raise "Expected content to change!" if updated_content == file.content
159
+
160
+ updated_content
161
+ end
162
+
163
+ def update_image(file)
164
+ old_images = old_yaml_images(file)
165
+ return if old_images.empty?
166
+
167
+ modified_content = file.content
168
+
169
+ old_images.each do |old_image|
170
+ old_image_regex = /^\s+(?:-\s)?image:\s+#{old_image}(?=\s|$)/
171
+ modified_content = modified_content.gsub(old_image_regex) do |old_img|
172
+ old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
173
+ end
174
+ end
175
+ modified_content
176
+ end
177
+
178
+ def new_yaml_image(file)
179
+ elt = dependency.requirements.find { |r| r[:file] == file.name }
180
+ prefix = elt.fetch(:source)[:registry] ? "#{elt.fetch(:source)[:registry]}/" : ""
181
+ digest = elt.fetch(:source)[:digest] ? "@#{elt.fetch(:source)[:digest]}" : ""
182
+ tag = elt.fetch(:source)[:tag] ? ":#{elt.fetch(:source)[:tag]}" : ""
183
+ "#{prefix}#{dependency.name}#{tag}#{digest}"
184
+ end
185
+
186
+ def old_yaml_images(file)
187
+ dependency.
188
+ previous_requirements.
189
+ select { |r| r[:file] == file.name }.map do |r|
190
+ prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
191
+ digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : ""
192
+ tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
193
+ "#{prefix}#{dependency.name}#{tag}#{digest}"
194
+ end
195
+ end
141
196
  end
142
197
  end
143
198
  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.211.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-23 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.211.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.211.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debase
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: parallel_tests
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.11.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.11.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rake
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +142,14 @@ dependencies:
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: 1.33.0
145
+ version: 1.35.1
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: 1.33.0
152
+ version: 1.35.1
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: ruby-debug-ide
141
155
  requirement: !ruby/object:Gem::Requirement