Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 640e48c

Browse files
committed
security: fetch the manifest more safely
In order to fetch vulnerabilities, the security scanner first has to gather the layers to be inspected. In our case, we fetch this information from the image manifest as given by the Registry. This commit adds some code so the fetching of this manifest is safer. For example, it will not freak out if a timeout was reached when requesting the manifest from the registry. Fixes #1743 Signed-off-by: Miquel Sabaté Solà <[email protected]>
1 parent 25f79e1 commit 640e48c

File tree

5 files changed

+49
-6
lines changed

5 files changed

+49
-6
lines changed

lib/portus/background/security_scanning.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ def execute!
4242
# progress.
4343
tag.update_vulnerabilities(scanned: Tag.statuses[:scan_working])
4444

45-
# Fetch vulnerabilities.
45+
# Fetch vulnerabilities. If there was an error and nil was returned,
46+
# simply skip this iteration.
4647
sec = ::Portus::Security.new(tag.repository.full_name, tag.name)
4748
vulns = sec.vulnerabilities
49+
next unless vulns
4850

4951
# And now update the tag with the vulnerabilities.
5052
dig = update_tag(tag, vulns)

lib/portus/http_helpers.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def handle_error(response, params = {})
9292
str += "\n"
9393
end
9494

95-
raise NotFoundError, str
95+
raise ::Portus::Errors::NotFoundError, str
9696
end
9797

9898
private

lib/portus/registry_client.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,13 @@ def reachable?
5252
# - The manifest digest.
5353
# - The manifest itself as a ruby hash.
5454
#
55-
# It will raise either a ManifestNotFoundError or a RuntimeError if
56-
# something goes wrong.
55+
# Three different exceptions might be raised:
56+
#
57+
# - ::Portus::RequestError: there was a request error with the registry
58+
# (e.g. a timeout).
59+
# - ::Portus::Errors::NotFoundError: the given manifest was not found.
60+
# - ::Portus::RegistryClient::ManifestError: there was an unknown problem
61+
# with the manifest.
5762
def manifest(repository, tag = "latest")
5863
res = safe_request("#{repository}/manifests/#{tag}", "get")
5964

lib/portus/security.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ def vulnerabilities
4343

4444
# First get all the layers composing the given image.
4545
client = Registry.get.client
46-
manifest = client.manifest(@repo, @tag)
46+
layers = fetch_layers(client)
47+
return unless layers
4748

4849
params = {
49-
layers: manifest.last["layers"].map { |l| l["digest"] },
50+
layers: layers.map { |l| l["digest"] },
5051
token: client.token,
5152
registry_url: client.base_url
5253
}
@@ -58,5 +59,16 @@ def vulnerabilities
5859
end
5960
res
6061
end
62+
63+
# Returns an array with the layers as given in the manifest. If an error
64+
# occured then nil will be returned and the error will be logged.
65+
def fetch_layers(rc)
66+
ary = rc.manifest(@repo, @tag)
67+
ary.last["layers"]
68+
rescue ::Portus::RequestError, ::Portus::Errors::NotFoundError,
69+
::Portus::RegistryClient::ManifestError => e
70+
Rails.logger.info e.to_s
71+
nil
72+
end
6173
end
6274
end

spec/lib/portus/security/clair_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,28 @@ def expect_cve_match(cves, given, expected)
161161

162162
expect(res[:clair]).to be_empty
163163
end
164+
165+
it "returns nil when a timeout occurred when fetching the manifest" do
166+
allow_any_instance_of(::Portus::RegistryClient).to receive(:manifest)
167+
.and_raise(::Portus::RequestError, exception: Net::ReadTimeout, message: "something")
168+
169+
clair = ::Portus::Security.new("coreos/dex", "unrelated")
170+
expect(clair.vulnerabilities).to be_nil
171+
end
172+
173+
it "returns nil when the manifest does not exist anymore on the registry" do
174+
allow_any_instance_of(::Portus::RegistryClient).to receive(:manifest)
175+
.and_raise(::Portus::Errors::NotFoundError, "something")
176+
177+
clair = ::Portus::Security.new("coreos/dex", "unrelated")
178+
expect(clair.vulnerabilities).to be_nil
179+
end
180+
181+
it "returns nil when there was something wrong with the manifest" do
182+
allow_any_instance_of(::Portus::RegistryClient).to receive(:manifest)
183+
.and_raise(::Portus::RegistryClient::ManifestError, "something")
184+
185+
clair = ::Portus::Security.new("coreos/dex", "unrelated")
186+
expect(clair.vulnerabilities).to be_nil
187+
end
164188
end

0 commit comments

Comments
 (0)