quayio-scanner 0.1.4 → 0.2.2

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: 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: []