quayio-scanner 0.1.1 → 0.2.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
- SHA1:
3
- metadata.gz: 968d7b786053e45ce5630a356686d164b8d3a342
4
- data.tar.gz: cfb261c4c614818113b1f5fe58da950b1d5f6762
2
+ SHA256:
3
+ metadata.gz: 62a2ef4b20397cec2c5a06cd141f61c4f9b0ab00ccdef0dbc235f39ada492232
4
+ data.tar.gz: a6bf579dc9491d3c4a8cc5d002148bed5bdadba1bb7b28ec17a3b4f6c429ac75
5
5
  SHA512:
6
- metadata.gz: d07176ec4c37a6c257fa45ae946a6724033513275ea610072a9beee69a84aa6440bd48c7dfa01a311da9a9adf36b80267ea1da51b6ac8cc2da09ff814cf88084
7
- data.tar.gz: ab925a960a68a6507468a6b7a0315d8029bcf714b5a2a4909c3d1779faebc317774c0760b9d67a0d7c995092b5a651563e7f0c460a3ddd1d620d23d002b96476
6
+ metadata.gz: fbf6cc4db97ce33820084256632012ecb42d50ea049d0f5f1a09cee19119edc92d97cf4a2b32ac5f12be0bd3594d71fd4bb7ed5050382fbf9c83287652afb529
7
+ data.tar.gz: c71f7a7dc9aaf747ea57485988a7fe261bddc4c69425fb7a33f07c550ee503c4bfd0e051496853333e9cba1ff5da6a2cc4f3cd4fa2d8819d3703d60937470a43
data/.gitignore CHANGED
@@ -8,3 +8,5 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /vendor/bundle
11
+ .project
12
+ .pydevproject
@@ -0,0 +1,11 @@
1
+ Style/FrozenStringLiteralComment:
2
+ Enabled: false
3
+
4
+ Style/Documentation:
5
+ Enabled: false
6
+
7
+ Metrics/MethodLength:
8
+ Max: 50
9
+
10
+ Metrics/BlockLength:
11
+ Max: 200
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
1
  require 'bundler/gem_tasks'
2
- task default: :spec
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RuboCop::RakeTask.new
6
+
7
+ task default: %i[rubocop]
@@ -36,9 +36,17 @@ class CheckContainerVulnerabilities < Sensu::Plugin::Check::CLI
36
36
  short: '-t TOKEN',
37
37
  long: '--quayio-token TOKEN'
38
38
 
39
+ option :whitelist,
40
+ description: 'Vulnerability whitelist',
41
+ short: '-w WHITELIST[,WHITELIST]',
42
+ long: '--whitelist WHITELIST[,WHITELIST]',
43
+ default: '',
44
+ proc: proc { |w| w.split(',') }
45
+
39
46
  def run
40
47
  status, message = Quayio::Scanner::Check.new(config[:docker_url],
41
- config[:quayio_token]).run
48
+ config[:quayio_token],
49
+ config[:whitelist]).run
42
50
 
43
51
  if status == :ok
44
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)
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) }
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,68 +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)
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'] &&
30
- f['Vulnerabilities']
31
- .detect { |v| RELEVANT_SEVERITIES.include?(v['Severity']) }
32
- end
33
- end
34
-
35
- def repo
36
- name.split(':').first.gsub(%r{quay.io\/}, '')
37
- end
38
-
39
- def tag
40
- name.split(':').last
41
- end
42
-
43
- def raw_image
44
- return @raw_image if defined? @raw_image
45
-
46
- @raw_image = begin
47
- JSON.parse(
48
- RestClient.get("https://quay.io/api/v1/repository/#{repo}/tag/#{tag}/images",
49
- authorization: "Bearer #{quayio_token}", accept: :json)
50
- )['images'].first
51
- rescue RestClient::ExceptionWithResponse => err
52
- return nil if err.http_code == 404 # ignore unknown repos
53
- raise err
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'])
38
+ end
54
39
  end
55
40
  end
56
41
 
57
42
  def raw_scan
58
- return @raw_scan if defined? @raw_scan
59
-
60
- @raw_scan = begin
61
- JSON.parse(
62
- RestClient.get("https://quay.io/api/v1/repository/#{repo}/image/#{raw_image['id']}/security?vulnerabilities=true",
63
- authorization: "Bearer #{quayio_token}", accept: :json)
64
- )
65
- end
43
+ @raw_scan ||= repository.scan
66
44
  end
67
45
  end
68
46
  end
@@ -0,0 +1,42 @@
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
+ )
32
+ return JSON.parse(response)
33
+ rescue RestClient::ExceptionWithResponse => e
34
+ raise e if e.http_code != 520 || attempt >= MAX_ATTEMPTS
35
+
36
+ sleep(rand(10))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  module Quayio
2
2
  module Scanner
3
- VERSION = '0.1.1'.freeze
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
4
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
3
  require 'quayio/scanner/version'
6
4
 
@@ -14,16 +12,19 @@ Gem::Specification.new do |spec|
14
12
  spec.homepage = 'https://github.com/aboutsource/quayio-scanner'
15
13
  spec.license = 'MIT'
16
14
 
15
+ spec.required_ruby_version = '>= 2.4.0'
16
+
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
18
  f.match(%r{^(test|spec|features)/})
19
19
  end
20
20
  spec.executables = Dir.glob('bin/**/*.rb').map { |file| File.basename(file) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'sensu-plugin', '~> 2.1'
24
23
  spec.add_dependency 'docker-api', '~> 1.33'
25
24
  spec.add_dependency 'rest-client', '~> 2.0'
26
- spec.add_development_dependency 'bundler', '~> 1.14'
25
+ spec.add_dependency 'sensu-plugin', '~> 2.1'
26
+ spec.add_development_dependency 'bundler'
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.7'
28
29
  spec.add_development_dependency 'rubocop', '~> 0.49'
29
30
  end
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quayio-scanner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
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: 2017-11-27 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: sensu-plugin
14
+ name: docker-api
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: '1.33'
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: '2.1'
26
+ version: '1.33'
27
27
  - !ruby/object:Gem::Dependency
28
- name: docker-api
28
+ name: rest-client
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.33'
33
+ version: '2.0'
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: '1.33'
40
+ version: '2.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rest-client
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.0'
47
+ version: '2.1'
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.0'
54
+ version: '2.1'
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: '0'
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: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.7'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rubocop
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -94,7 +108,7 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0.49'
97
- description:
111
+ description:
98
112
  email:
99
113
  - benjamin.meichsner@aboutsource.net
100
114
  executables:
@@ -103,6 +117,7 @@ extensions: []
103
117
  extra_rdoc_files: []
104
118
  files:
105
119
  - ".gitignore"
120
+ - ".rubocop.yml"
106
121
  - Gemfile
107
122
  - LICENSE.txt
108
123
  - README.md
@@ -111,13 +126,14 @@ files:
111
126
  - lib/quayio/scanner.rb
112
127
  - lib/quayio/scanner/check.rb
113
128
  - lib/quayio/scanner/image.rb
129
+ - lib/quayio/scanner/repository.rb
114
130
  - lib/quayio/scanner/version.rb
115
131
  - quayio-scanner.gemspec
116
132
  homepage: https://github.com/aboutsource/quayio-scanner
117
133
  licenses:
118
134
  - MIT
119
135
  metadata: {}
120
- post_install_message:
136
+ post_install_message:
121
137
  rdoc_options: []
122
138
  require_paths:
123
139
  - lib
@@ -125,16 +141,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
141
  requirements:
126
142
  - - ">="
127
143
  - !ruby/object:Gem::Version
128
- version: '0'
144
+ version: 2.4.0
129
145
  required_rubygems_version: !ruby/object:Gem::Requirement
130
146
  requirements:
131
147
  - - ">="
132
148
  - !ruby/object:Gem::Version
133
149
  version: '0'
134
150
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.6.13
137
- signing_key:
151
+ rubygems_version: 3.1.4
152
+ signing_key:
138
153
  specification_version: 4
139
154
  summary: Scan quay.io for vulnerabilties in running docker containers.
140
155
  test_files: []