quayio-scanner 0.1.5 → 0.2.3

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: 5df176d74b68e0b31a5408b2be36bac9c0cb2171650acb160cfedd90a143de2c
4
- data.tar.gz: aab6b7098d7a848294613428d8b514589c949f5d9f1fdf9041af06932d12856c
3
+ metadata.gz: ec3e0ce31e72f8fb58ce5bb62ec17af8395f8cbb0dfe6825bd8409e8388167a3
4
+ data.tar.gz: af37eec22d47077ad5c6cdb761b18071864ab628d459b15ed7130c645a09edc4
5
5
  SHA512:
6
- metadata.gz: 1da63def2a66714fff1b69b709150b444a0b149f7da03c1a9bcdec87f895a1b73a44887d9ded63c928dca958f5edc82ecc06a074957c3103f96958dccb0f2183
7
- data.tar.gz: a0c809340d55c456aa639ecebe00926722ece6f156efee325f713fcb25ef371ca8cc44ad828665767175f31fce558300665eb976ebbb8d9d8ac346d386cd1b96
6
+ metadata.gz: 194cca2abb4781442a8730a9ad0afb5097bc0e63d9dcd1a4c1dc0c92c6832af5020fd3e8dceb44fa5cd9c56da8bff986669146cd2ba8c141c165203fa5d09ee2
7
+ data.tar.gz: 4ac42a474343fae8c5ce01141cf85ebf514a3d37050fc93f28f7cb5202c231ab1976b94112f7aff278a00c3fac3082a7f1f454e0291ac60b9e7be764689a8d1c
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
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
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7.0
data/Gemfile.lock ADDED
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ quayio-scanner (0.2.3)
5
+ docker-api (~> 1.33)
6
+ rest-client (~> 2.1)
7
+ sensu-plugin (~> 4.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ ast (2.4.1)
13
+ diff-lcs (1.4.4)
14
+ docker-api (1.34.2)
15
+ excon (>= 0.47.0)
16
+ multi_json
17
+ domain_name (0.5.20190701)
18
+ unf (>= 0.0.5, < 1.0.0)
19
+ excon (0.85.0)
20
+ http-accept (1.7.0)
21
+ http-cookie (1.0.4)
22
+ domain_name (~> 0.5)
23
+ json (2.5.1)
24
+ mime-types (3.3.1)
25
+ mime-types-data (~> 3.2015)
26
+ mime-types-data (3.2021.0704)
27
+ mixlib-cli (1.7.0)
28
+ multi_json (1.15.0)
29
+ netrc (0.11.0)
30
+ parallel (1.19.2)
31
+ parser (2.7.2.0)
32
+ ast (~> 2.4.1)
33
+ rainbow (3.0.0)
34
+ rake (10.5.0)
35
+ regexp_parser (1.8.2)
36
+ rest-client (2.1.0)
37
+ http-accept (>= 1.7.0, < 2.0)
38
+ http-cookie (>= 1.0.2, < 2.0)
39
+ mime-types (>= 1.16, < 4.0)
40
+ netrc (~> 0.8)
41
+ rexml (3.2.4)
42
+ rspec (3.9.0)
43
+ rspec-core (~> 3.9.0)
44
+ rspec-expectations (~> 3.9.0)
45
+ rspec-mocks (~> 3.9.0)
46
+ rspec-core (3.9.3)
47
+ rspec-support (~> 3.9.3)
48
+ rspec-expectations (3.9.3)
49
+ diff-lcs (>= 1.2.0, < 2.0)
50
+ rspec-support (~> 3.9.0)
51
+ rspec-mocks (3.9.1)
52
+ diff-lcs (>= 1.2.0, < 2.0)
53
+ rspec-support (~> 3.9.0)
54
+ rspec-support (3.9.4)
55
+ rubocop (0.93.1)
56
+ parallel (~> 1.10)
57
+ parser (>= 2.7.1.5)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ regexp_parser (>= 1.8)
60
+ rexml
61
+ rubocop-ast (>= 0.6.0)
62
+ ruby-progressbar (~> 1.7)
63
+ unicode-display_width (>= 1.4.0, < 2.0)
64
+ rubocop-ast (1.1.0)
65
+ parser (>= 2.7.1.5)
66
+ ruby-progressbar (1.10.1)
67
+ sensu-plugin (4.0.0)
68
+ json (< 3.0.0)
69
+ mixlib-cli (~> 1.5)
70
+ unf (0.1.4)
71
+ unf_ext
72
+ unf_ext (0.0.7.7)
73
+ unicode-display_width (1.7.0)
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ bundler (~> 2.2)
80
+ quayio-scanner!
81
+ rake (~> 10.0)
82
+ rspec (~> 3.7)
83
+ rubocop (~> 0.49)
84
+
85
+ BUNDLED WITH
86
+ 2.2.23
data/README.md CHANGED
@@ -26,3 +26,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/abouts
26
26
  ## License
27
27
 
28
28
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
29
+
30
+ ## Security
31
+
32
+ * [Snyk](https://app.snyk.io/org/about-source/project/6eb2d381-87e7-49c4-a47f-ccad97f33ae3)
@@ -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,78 +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)
8
- MAX_ATTEMPTS = 5
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
9
18
 
10
19
  def vulnerable?
11
- quayio? && image_exists? && scanned? && high_vulnerabilities_present?
20
+ quayio? && scanned? && vulnerabilities_present?
12
21
  end
13
22
 
14
23
  private
15
24
 
16
25
  def quayio?
17
- name.match(%r{^quay.io\/})
18
- end
19
-
20
- def image_exists?
21
- raw_image
26
+ # safe guard, do not trust QUAY_IO_REPO_NAME regex match
27
+ !!name.match(%r{^quay.io\/})
22
28
  end
23
29
 
24
30
  def scanned?
25
31
  raw_scan['status'] == 'scanned'
26
32
  end
27
33
 
28
- def high_vulnerabilities_present?
29
- raw_scan['data']['Layer']['Features'].detect do |f|
30
- f['Vulnerabilities'] && f['Vulnerabilities'].detect do |v|
31
- RELEVANT_SEVERITIES.include?(v['Severity']) &&
32
- !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'])
33
38
  end
34
39
  end
35
40
  end
36
41
 
37
- def repo
38
- name.split(':').first.gsub(%r{quay.io\/}, '')
39
- end
40
-
41
- def tag
42
- name.split(':').last
43
- end
44
-
45
- def raw_image
46
- return @raw_image if defined? @raw_image
47
-
48
- (1..MAX_ATTEMPTS).each do |attempt|
49
- begin
50
- response = RestClient.get(
51
- "https://quay.io/api/v1/repository/#{repo}/tag/#{tag}/images",
52
- authorization: "Bearer #{quayio_token}",
53
- accept: :json)
54
- rescue RestClient::ExceptionWithResponse => err
55
- return nil if err.http_code == 404 # ignore unknown repos
56
- if err.http_code == 520 and attempt < MAX_ATTEMPTS
57
- sleep(rand(10))
58
- next
59
- end
60
- raise err
61
- end
62
- @raw_image = JSON.parse(response)['images'].first
63
- return @raw_image
64
- end
65
- end
66
-
67
42
  def raw_scan
68
- return @raw_scan if defined? @raw_scan
69
-
70
- @raw_scan = begin
71
- JSON.parse(
72
- RestClient.get("https://quay.io/api/v1/repository/#{repo}/image/#{raw_image['id']}/security?vulnerabilities=true",
73
- authorization: "Bearer #{quayio_token}", accept: :json)
74
- )
75
- end
43
+ @raw_scan ||= repository.scan
76
44
  end
77
45
  end
78
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.5'.freeze
3
+ VERSION = '0.2.3'.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.5
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Meichsner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-31 00:00:00.000000000 Z
11
+ date: 2021-08-03 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
@@ -117,7 +117,10 @@ extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
120
+ - ".rubocop.yml"
121
+ - ".ruby-version"
120
122
  - Gemfile
123
+ - Gemfile.lock
121
124
  - LICENSE.txt
122
125
  - README.md
123
126
  - Rakefile
@@ -125,6 +128,7 @@ files:
125
128
  - lib/quayio/scanner.rb
126
129
  - lib/quayio/scanner/check.rb
127
130
  - lib/quayio/scanner/image.rb
131
+ - lib/quayio/scanner/repository.rb
128
132
  - lib/quayio/scanner/version.rb
129
133
  - quayio-scanner.gemspec
130
134
  homepage: https://github.com/aboutsource/quayio-scanner
@@ -139,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
143
  requirements:
140
144
  - - ">="
141
145
  - !ruby/object:Gem::Version
142
- version: '0'
146
+ version: 2.3.0
143
147
  required_rubygems_version: !ruby/object:Gem::Requirement
144
148
  requirements:
145
149
  - - ">="