quayio-scanner 0.1.4 → 0.2.2

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: 5870d44003c0600c96604760935938ecad1576818ae94f997e01ba5172390952
4
- data.tar.gz: e1476f3dc385413bfbfb47b26345710774889913767bb0c9d9fcd2ab00996c6e
3
+ metadata.gz: 9682167dd4f87703d927a236079beb847abd6d787e1e95b63e9517c8e79572cf
4
+ data.tar.gz: 37b75014b47d09dc7ccd293526a98b90d3d7ef81d8daa4302da1f24dbf51a441
5
5
  SHA512:
6
- metadata.gz: e85522f956cca1178c249269124af889f9937c75e442894cddcd0c7e45e9d191d7fa1cff688e15e95eb28f62ae8de3b0fe6bdb285ef4d33c3053eb2b041e563d
7
- data.tar.gz: 1b763e457f3ed56c1d8a812fb567fd93a38e31c1d93f1dc7a6108ec455ca0263cc4c552eae3bfdf7150f11b8c29816b77d72303f4f132c77567e51a4366db95b
6
+ metadata.gz: 27ae72de19649c10f00d11e6dc461c5aea63cdb98449039354fc8345686f6def8c984ae6a6f6cf1a3ea24034a04fe1a229bfa078ea21bbae261f1c268373f9a6
7
+ data.tar.gz: 79915aaeb679343ad481d0a1b1171bb3c6a62bb841ffe657fbe18ac57550310eecfe2c9ce33b069313564f5a0ce581283e1d70aec38d1f74b528c05876994c53
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+
4
+ Style/FrozenStringLiteralComment:
5
+ Enabled: false
6
+
7
+ Style/Documentation:
8
+ Enabled: false
9
+
10
+ Metrics/MethodLength:
11
+ Max: 50
12
+
13
+ Metrics/BlockLength:
14
+ Max: 200
@@ -44,8 +44,9 @@ class CheckContainerVulnerabilities < Sensu::Plugin::Check::CLI
44
44
  proc: proc { |w| w.split(',') }
45
45
 
46
46
  def run
47
- status, message = Quayio::Scanner::Check.new(
48
- config[:docker_url], config[:quayio_token], config[:whitelist]).run
47
+ status, message = Quayio::Scanner::Check.new(config[:docker_url],
48
+ config[:quayio_token],
49
+ config[:whitelist]).run
49
50
 
50
51
  if status == :ok
51
52
  ok message
@@ -1,5 +1,7 @@
1
- require 'quayio/scanner/version'
2
1
  require 'quayio/scanner/check'
2
+ require 'quayio/scanner/image'
3
+ require 'quayio/scanner/repository'
4
+ require 'quayio/scanner/version'
3
5
 
4
6
  module Quayio
5
7
  module Scanner
@@ -1,19 +1,10 @@
1
- require 'quayio/scanner/image'
2
1
  require 'docker'
3
2
 
4
3
  module Quayio
5
4
  module Scanner
6
- class Check < Struct.new(:docker_url, :quayio_token, :whitelist)
5
+ Check = Struct.new(:docker_url, :quayio_token, :whitelist) do
7
6
  def run
8
7
  Docker.url = docker_url
9
- containers = Docker::Container.all
10
- .map { |dc| dc.json['Config']['Image'] }
11
- .uniq
12
-
13
- vulnerable_images = containers
14
- .map { |container| Image.new(container, quayio_token, whitelist) }
15
- .select(&:vulnerable?)
16
- .map(&:name)
17
8
 
18
9
  if vulnerable_images.empty?
19
10
  [:ok, "#{containers.size} Containers are ok"]
@@ -21,6 +12,22 @@ module Quayio
21
12
  [:critical, "The images are insecure: #{vulnerable_images.join(', ')}"]
22
13
  end
23
14
  end
15
+
16
+ private
17
+
18
+ def containers
19
+ Docker::Container
20
+ .all
21
+ .map { |dc| dc.json['Config']['Image'] }
22
+ .uniq
23
+ end
24
+
25
+ def vulnerable_images
26
+ containers
27
+ .map { |container| Image.new(container, quayio_token, whitelist) }
28
+ .select(&:vulnerable?)
29
+ .map(&:name)
30
+ end
24
31
  end
25
32
  end
26
33
  end
@@ -1,69 +1,46 @@
1
- require 'json'
2
- require 'rest-client'
3
-
4
1
  module Quayio
5
2
  module Scanner
6
- class Image < Struct.new(:name, :quayio_token, :whitelist)
7
- RELEVANT_SEVERITIES = %w(High Critical)
3
+ class Image
4
+ RELEVANT_SEVERITIES = %w[High Critical].freeze
5
+ QUAY_IO_REPO_NAME = %r{quay.io\/(?<org>[\w-]+)\/(?<repo>[\w-]+):(?<tag>[\w\.-]+)}.freeze
6
+
7
+ attr_reader :name, :whitelist, :repository
8
+
9
+ def initialize(name, quayio_token, whitelist)
10
+ @name = name
11
+ @whitelist = whitelist
12
+
13
+ @name.match(QUAY_IO_REPO_NAME) do |r|
14
+ org, repo, tag = r.captures
15
+ @repository = Repository.new(quayio_token, org, repo, tag)
16
+ end
17
+ end
8
18
 
9
19
  def vulnerable?
10
- quayio? && image_exists? && scanned? && high_vulnerabilities_present?
20
+ quayio? && scanned? && vulnerabilities_present?
11
21
  end
12
22
 
13
23
  private
14
24
 
15
25
  def quayio?
16
- name.match(%r{^quay.io\/})
17
- end
18
-
19
- def image_exists?
20
- raw_image
26
+ # safe guard, do not trust QUAY_IO_REPO_NAME regex match
27
+ !!name.match(%r{^quay.io\/})
21
28
  end
22
29
 
23
30
  def scanned?
24
31
  raw_scan['status'] == 'scanned'
25
32
  end
26
33
 
27
- def high_vulnerabilities_present?
28
- raw_scan['data']['Layer']['Features'].detect do |f|
29
- f['Vulnerabilities'] && f['Vulnerabilities'].detect do |v|
30
- RELEVANT_SEVERITIES.include?(v['Severity']) &&
31
- !whitelist.include?(v['Name'])
34
+ def vulnerabilities_present?
35
+ !!raw_scan['data']['Layer']['Features'].detect do |f|
36
+ f['Vulnerabilities']&.detect do |v|
37
+ RELEVANT_SEVERITIES.include?(v['Severity']) && !whitelist.include?(v['Name'])
32
38
  end
33
39
  end
34
40
  end
35
41
 
36
- def repo
37
- name.split(':').first.gsub(%r{quay.io\/}, '')
38
- end
39
-
40
- def tag
41
- name.split(':').last
42
- end
43
-
44
- def raw_image
45
- return @raw_image if defined? @raw_image
46
-
47
- @raw_image = begin
48
- JSON.parse(
49
- RestClient.get("https://quay.io/api/v1/repository/#{repo}/tag/#{tag}/images",
50
- authorization: "Bearer #{quayio_token}", accept: :json)
51
- )['images'].first
52
- rescue RestClient::ExceptionWithResponse => err
53
- return nil if err.http_code == 404 # ignore unknown repos
54
- raise err
55
- end
56
- end
57
-
58
42
  def raw_scan
59
- return @raw_scan if defined? @raw_scan
60
-
61
- @raw_scan = begin
62
- JSON.parse(
63
- RestClient.get("https://quay.io/api/v1/repository/#{repo}/image/#{raw_image['id']}/security?vulnerabilities=true",
64
- authorization: "Bearer #{quayio_token}", accept: :json)
65
- )
66
- end
43
+ @raw_scan ||= repository.scan
67
44
  end
68
45
  end
69
46
  end
@@ -0,0 +1,46 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module Quayio
5
+ module Scanner
6
+ Repository = Struct.new(:quayio_token, :org, :repo, :tag) do
7
+ MAX_ATTEMPTS = 5
8
+
9
+ def id
10
+ @id ||= fetch_id
11
+ end
12
+
13
+ def scan
14
+ api_call("/image/#{id}/security?vulnerabilities=true")
15
+ end
16
+
17
+ private
18
+
19
+ def fetch_id
20
+ result = api_call("/tag/#{tag}/images")
21
+ (result['images'].first)['id']
22
+ end
23
+
24
+ def api_call(uri)
25
+ (1..Float::INFINITY).each do |attempt|
26
+ begin
27
+ response = RestClient.get(
28
+ "https://quay.io/api/v1/repository/#{org}/#{repo}#{uri}",
29
+ authorization: "Bearer #{quayio_token}",
30
+ accept: :json,
31
+ open_timeout: 15
32
+ )
33
+ return JSON.parse(response)
34
+ rescue RestClient::Exception => e
35
+ raise e if attempt >= MAX_ATTEMPTS
36
+
37
+ # retry later, if we hit cdn rate limiting or on connection errors
38
+ raise e unless e.http_code == 520 || e.http_code.nil?
39
+
40
+ sleep(rand(10))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,5 +1,5 @@
1
1
  module Quayio
2
2
  module Scanner
3
- VERSION = '0.1.4'.freeze
3
+ VERSION = '0.2.2'.freeze
4
4
  end
5
5
  end
@@ -12,6 +12,8 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = 'https://github.com/aboutsource/quayio-scanner'
13
13
  spec.license = 'MIT'
14
14
 
15
+ spec.required_ruby_version = '>= 2.3.0'
16
+
15
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
18
  f.match(%r{^(test|spec|features)/})
17
19
  end
@@ -19,9 +21,9 @@ Gem::Specification.new do |spec|
19
21
  spec.require_paths = ['lib']
20
22
 
21
23
  spec.add_dependency 'docker-api', '~> 1.33'
22
- spec.add_dependency 'rest-client', '~> 2.0'
23
- spec.add_dependency 'sensu-plugin', '~> 2.1'
24
- spec.add_development_dependency 'bundler', '~> 1.14'
24
+ spec.add_dependency 'rest-client', '~> 2.1'
25
+ spec.add_dependency 'sensu-plugin', '~> 4.0'
26
+ spec.add_development_dependency 'bundler', '~> 2.2'
25
27
  spec.add_development_dependency 'rake', '~> 10.0'
26
28
  spec.add_development_dependency 'rspec', '~> 3.7'
27
29
  spec.add_development_dependency 'rubocop', '~> 0.49'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quayio-scanner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Meichsner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-10 00:00:00.000000000 Z
11
+ date: 2021-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docker-api
@@ -30,42 +30,42 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '2.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '2.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: sensu-plugin
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.1'
47
+ version: '4.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.1'
54
+ version: '4.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.14'
61
+ version: '2.2'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.14'
68
+ version: '2.2'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.49'
111
- description:
111
+ description:
112
112
  email:
113
113
  - benjamin.meichsner@aboutsource.net
114
114
  executables:
@@ -117,6 +117,7 @@ extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
120
+ - ".rubocop.yml"
120
121
  - Gemfile
121
122
  - LICENSE.txt
122
123
  - README.md
@@ -125,13 +126,14 @@ files:
125
126
  - lib/quayio/scanner.rb
126
127
  - lib/quayio/scanner/check.rb
127
128
  - lib/quayio/scanner/image.rb
129
+ - lib/quayio/scanner/repository.rb
128
130
  - lib/quayio/scanner/version.rb
129
131
  - quayio-scanner.gemspec
130
132
  homepage: https://github.com/aboutsource/quayio-scanner
131
133
  licenses:
132
134
  - MIT
133
135
  metadata: {}
134
- post_install_message:
136
+ post_install_message:
135
137
  rdoc_options: []
136
138
  require_paths:
137
139
  - lib
@@ -139,15 +141,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
141
  requirements:
140
142
  - - ">="
141
143
  - !ruby/object:Gem::Version
142
- version: '0'
144
+ version: 2.3.0
143
145
  required_rubygems_version: !ruby/object:Gem::Requirement
144
146
  requirements:
145
147
  - - ">="
146
148
  - !ruby/object:Gem::Version
147
149
  version: '0'
148
150
  requirements: []
149
- rubygems_version: 3.1.2
150
- signing_key:
151
+ rubygems_version: 3.2.21
152
+ signing_key:
151
153
  specification_version: 4
152
154
  summary: Scan quay.io for vulnerabilties in running docker containers.
153
155
  test_files: []