cvelist 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +6 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +10 -0
  8. data/Gemfile +13 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +82 -0
  11. data/Rakefile +13 -0
  12. data/benchmark.rb +38 -0
  13. data/cvelist.gemspec +61 -0
  14. data/gemspec.yml +21 -0
  15. data/lib/cvelist.rb +2 -0
  16. data/lib/cvelist/cve.rb +83 -0
  17. data/lib/cvelist/directory.rb +80 -0
  18. data/lib/cvelist/exceptions.rb +31 -0
  19. data/lib/cvelist/malformed_cve.rb +42 -0
  20. data/lib/cvelist/range_dir.rb +121 -0
  21. data/lib/cvelist/repository.rb +240 -0
  22. data/lib/cvelist/version.rb +4 -0
  23. data/lib/cvelist/year_dir.rb +178 -0
  24. data/spec/cve_methods_examples.rb +130 -0
  25. data/spec/cve_spec.rb +51 -0
  26. data/spec/cvelist_spec.rb +8 -0
  27. data/spec/directory_spec.rb +83 -0
  28. data/spec/fixtures/CVE-2020-1994.json +140 -0
  29. data/spec/fixtures/cvelist/.gitkeep +0 -0
  30. data/spec/fixtures/cvelist/1999/.gitkeep +0 -0
  31. data/spec/fixtures/cvelist/2000/.gitkeep +0 -0
  32. data/spec/fixtures/cvelist/2001/.gitkeep +0 -0
  33. data/spec/fixtures/cvelist/2002/.gitkeep +0 -0
  34. data/spec/fixtures/cvelist/2003/.gitkeep +0 -0
  35. data/spec/fixtures/cvelist/2004/.gitkeep +0 -0
  36. data/spec/fixtures/cvelist/2005/.gitkeep +0 -0
  37. data/spec/fixtures/cvelist/2006/.gitkeep +0 -0
  38. data/spec/fixtures/cvelist/2007/.gitkeep +0 -0
  39. data/spec/fixtures/cvelist/2008/.gitkeep +0 -0
  40. data/spec/fixtures/cvelist/2009/.gitkeep +0 -0
  41. data/spec/fixtures/cvelist/2010/.gitkeep +0 -0
  42. data/spec/fixtures/cvelist/2011/.gitkeep +0 -0
  43. data/spec/fixtures/cvelist/2012/.gitkeep +0 -0
  44. data/spec/fixtures/cvelist/2013/.gitkeep +0 -0
  45. data/spec/fixtures/cvelist/2014/.gitkeep +0 -0
  46. data/spec/fixtures/cvelist/2015/.gitkeep +0 -0
  47. data/spec/fixtures/cvelist/2016/.gitkeep +0 -0
  48. data/spec/fixtures/cvelist/2017/.gitkeep +0 -0
  49. data/spec/fixtures/cvelist/2018/.gitkeep +0 -0
  50. data/spec/fixtures/cvelist/2019/.gitkeep +0 -0
  51. data/spec/fixtures/cvelist/2020/.gitkeep +0 -0
  52. data/spec/fixtures/cvelist/2021/.gitkeep +0 -0
  53. data/spec/fixtures/cvelist/2021/0xxx/.gitkeep +0 -0
  54. data/spec/fixtures/cvelist/2021/1xxx/.gitkeep +0 -0
  55. data/spec/fixtures/cvelist/2021/20xxx/.gitkeep +0 -0
  56. data/spec/fixtures/cvelist/2021/21xxx/.gitkeep +0 -0
  57. data/spec/fixtures/cvelist/2021/2xxx/.gitkeep +0 -0
  58. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2000.json +18 -0
  59. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2001.json +18 -0
  60. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2002.json +18 -0
  61. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2003.json +18 -0
  62. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2004.json +18 -0
  63. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2005.json +18 -0
  64. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2006.json +18 -0
  65. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2007.json +18 -0
  66. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2008.json +18 -0
  67. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2009.json +18 -0
  68. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2998.json +3 -0
  69. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2999.json +2 -0
  70. data/spec/range_dir_spec.rb +55 -0
  71. data/spec/repository_spec.rb +248 -0
  72. data/spec/spec_helper.rb +4 -0
  73. data/spec/year_dir_spec.rb +96 -0
  74. metadata +165 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d5f0ca46bdd362287b4845fed77c588ef591bb86e11219f3b248b70470d5e1b0
4
+ data.tar.gz: 1507b6e252a6b4c4f721e842a23ae8d20d65c08b6644a4fce8fe31eb388fa8b4
5
+ SHA512:
6
+ metadata.gz: ccc4c2da9456bc2708bb7f30d59042d4e77d32fc12bc0524e81b23f93dcfd87c4c7bfa315f66a0840fe4c66d761daf780823486ded35fda482968b462804dbf5
7
+ data.tar.gz: ad330548abb01672b25493a0c0bd84d319b172c175f7cdd7156efed1e150eeb3e4a6bb2556adaf3be9b0c21b402539e0c3476220235ec246d4790b43eddba747
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ tests:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby:
12
+ # - 2.4
13
+ # - 2.5
14
+ # - 2.6
15
+ - 2.7
16
+ - 3.0
17
+ # TODO: uncomment when jruby supports ruby >= 2.7
18
+ # - jruby
19
+ name: Ruby ${{ matrix.ruby }}
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+ - name: Set up Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ - name: Install dependencies
27
+ run: bundle install --jobs 4 --retry 3
28
+ - name: Run tests
29
+ run: bundle exec rake test
@@ -0,0 +1,6 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
6
+ /vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
@@ -0,0 +1 @@
1
+ --markup markdown --title "CVEList Documentation" --protected
@@ -0,0 +1,10 @@
1
+ ### 0.1.0 / 2021-01-14
2
+
3
+ * Initial release:
4
+ * Supports [CVE JSON Schema v4.0][1].
5
+ * Added {CVEList::Repository}.
6
+ * Added {CVEList::YearDir}.
7
+ * Added {CVEList::RangeDir}.
8
+ * Added {CVEList::CVE}.
9
+ * Added {CVEList::MalformedCVE}.
10
+
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ gem 'rubygems-tasks', '~> 0.2'
8
+
9
+ gem 'rspec', '~> 3.0'
10
+
11
+ gem 'kramdown'
12
+ gem 'yard', '~> 0.9'
13
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020-2021 Hal Brodigan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # cvelist.rb
2
+
3
+ * [Homepage](https://github.com/postmodern/cvelist.rb#readme)
4
+ * [Issues](https://github.com/postmodern/cvelist.rb/issues)
5
+ * [Documentation](http://rubydoc.info/gems/cvelist/frames)
6
+ * [Email](mailto:postmodern.mod3 at gmail.com)
7
+
8
+ ## Description
9
+
10
+ A Ruby library for parsing the CVE JSON in the [cvelist] git repository.
11
+
12
+ ## Features
13
+
14
+ * Supports downloading/updating [cvelist] Git repository.
15
+ * Supports [CVE JSON Schema v4.0][1]
16
+
17
+ ## Examples
18
+
19
+ require 'cvelist'
20
+
21
+ Cloning the [cvelist] repository:
22
+
23
+ repo = CVEList::Repository.clone('path/to/cvelist')
24
+
25
+ Using an existing [cvelist] repository:
26
+
27
+ repo = CVEList::Repository.new('path/to/cvelist')
28
+
29
+ Updating an existing [cvelist] repository:
30
+
31
+ repo.pull!
32
+
33
+ Get the total number of CVEs in the repository:
34
+
35
+ repo.size
36
+
37
+ Access an individual [CVE] in the repository:
38
+
39
+ repo['CVE-2020-0001']
40
+
41
+ Enumerating over every [CVE] in the repository:
42
+
43
+ repo.each do |cve|
44
+ puts cve.id
45
+ end
46
+
47
+ Enumerating over every [CVE] in a certain year:
48
+
49
+ repo.year(2020).each do |cve|
50
+ puts cve.id
51
+ end
52
+
53
+ ## Requirements
54
+
55
+ * [multi_json] ~> 1.0
56
+ * [cve_schema] ~> 0.1
57
+
58
+ ## Install
59
+
60
+ $ gem install cvelist
61
+
62
+ ## Benchmark
63
+
64
+ Warming up the disk cache with a first run. This may take a while ...
65
+ Parsing all 192879 CVE .json files ...
66
+
67
+ Total: 18.285097 1.651155 19.936252 ( 20.144566)
68
+ Avg: 0.000095 0.000009 0.000103 ( 0.000104)
69
+
70
+ ## Copyright
71
+
72
+ Copyright (c) 2020-2021 Hal Brodigan
73
+
74
+ See {file:LICENSE.txt} for details.
75
+
76
+ [cvelist]: https://github.com/CVEProject/cvelist
77
+ [1]: https://github.com/CVEProject/cve-schema/blob/master/schema/v4.0/DRAFT-JSON-file-format-v4.md
78
+
79
+ [multi_json]: https://github.com/intridea/multi_json#readme
80
+ [cve_schema]: https://github.com/postmodern/cve_schema.rb#readme
81
+
82
+ [CVE]: https://rubydoc.info/gems/cve_schema/CVESchema/CVE/frames
@@ -0,0 +1,13 @@
1
+ require 'rake'
2
+ require 'rubygems/tasks'
3
+ Gem::Tasks.new
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new
7
+
8
+ task :test => :spec
9
+ task :default => :spec
10
+
11
+ require 'yard'
12
+ YARD::Rake::YardocTask.new
13
+ task :doc => :yard
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'cvelist/repository'
5
+ require 'json'
6
+ require 'benchmark'
7
+
8
+ CVELIST = ENV.fetch('CVELIST',File.join(Gem.user_home,'src','cvelist'))
9
+
10
+ repo = if File.directory?(CVELIST)
11
+ CVEList::Repository.new(CVELIST)
12
+ else
13
+ puts "Cloning #{CVEList::Repository::URL} into #{CVELIST} ..."
14
+ CVEList::Repository.clone(CVELIST)
15
+ end
16
+
17
+ begin
18
+ n = repo.total_cves
19
+
20
+ puts "Warming up the disk cache with a first run. This may take a while ..."
21
+ repo.each do |cve|
22
+ # no-op
23
+ end
24
+
25
+ puts "Parsing all #{n} CVE .json files ..."
26
+
27
+ results = Benchmark.measure do
28
+ repo.each do |cve|
29
+ # no-op
30
+ end
31
+ end
32
+
33
+ puts
34
+ puts "Total:\t#{results}"
35
+ puts "Avg:\t#{results / n}"
36
+ rescue Interrupt
37
+ exit 130
38
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'cvelist/version'
14
+ CVEList::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+
30
+ gem.executables = gemspec.fetch('executables') do
31
+ glob['bin/*'].map { |path| File.basename(path) }
32
+ end
33
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
34
+
35
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
36
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
37
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
38
+
39
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
40
+ %w[ext lib].select { |dir| File.directory?(dir) }
41
+ })
42
+
43
+ gem.requirements = Array(gemspec['requirements'])
44
+ gem.required_ruby_version = gemspec['required_ruby_version']
45
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
46
+ gem.post_install_message = gemspec['post_install_message']
47
+
48
+ split = lambda { |string| string.split(/,\s*/) }
49
+
50
+ if gemspec['dependencies']
51
+ gemspec['dependencies'].each do |name,versions|
52
+ gem.add_dependency(name,split[versions])
53
+ end
54
+ end
55
+
56
+ if gemspec['development_dependencies']
57
+ gemspec['development_dependencies'].each do |name,versions|
58
+ gem.add_development_dependency(name,split[versions])
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ name: cvelist
2
+ summary: Parses cvelist JSON
3
+ description: |
4
+ A Ruby library for parsing the CVE JSON in the cvelist git repository.
5
+ license: MIT
6
+ authors: Postmodern
7
+ email: postmodern.mod3@gmail.com
8
+ homepage: https://github.com/postmodern/cvelist.rb#readme
9
+
10
+ metadata:
11
+ documentation_uri: https://rubydoc.info/gems/cvelist
12
+ source_code_uri: https://github.com/postmodern/cvelist.rb
13
+ bug_tracker_uri: https://github.com/postmodern/cvelist.rb/issues
14
+ changelog_uri: https://github.com/postmodern/cvelist.rb/blob/main/ChangeLog.md
15
+
16
+ dependencies:
17
+ multi_json: ~> 1.0
18
+ cve_schema: ~> 0.1
19
+
20
+ development_dependencies:
21
+ bundler: ~> 2.0
@@ -0,0 +1,2 @@
1
+ require 'cvelist/repository'
2
+ require 'cvelist/version'
@@ -0,0 +1,83 @@
1
+ require 'cvelist/exceptions'
2
+ require 'cve_schema/cve'
3
+ require 'multi_json'
4
+
5
+ module CVEList
6
+ #
7
+ # @see https://rubydoc.info/gems/cve_schema/CVESchema/CVE/frames
8
+ #
9
+ class CVE < CVESchema::CVE
10
+
11
+ # The path to the CVE JSON.
12
+ #
13
+ # @return [String]
14
+ attr_reader :path
15
+
16
+ #
17
+ # Initializes the CVE.
18
+ #
19
+ # @param [String] path
20
+ # The path to the CVE JSON.
21
+ #
22
+ def initialize(path, **kwargs)
23
+ super(**kwargs)
24
+
25
+ @path = path
26
+ end
27
+
28
+ #
29
+ # Parses the given JSON.
30
+ #
31
+ # @param [String] json
32
+ # The raw JSON.
33
+ #
34
+ # @return [Hash{String => Object}]
35
+ # The parsed JSON.
36
+ #
37
+ # @raise [InvalidJSON]
38
+ # Could not parse the JSON in the given file.
39
+ #
40
+ # @api semipublic
41
+ #
42
+ def self.parse(json)
43
+ MultiJson.load(json)
44
+ rescue MultiJson::ParseError => error
45
+ raise(InvalidJSON,error.message)
46
+ end
47
+
48
+ #
49
+ # Parses the JSON in the given file.
50
+ #
51
+ # @param [String] file
52
+ # The path to the file.
53
+ #
54
+ # @return [Hash{String => Object}]
55
+ # The parsed JSON.
56
+ #
57
+ # @raise [InvalidJSON]
58
+ # Could not parse the JSON in the given file.
59
+ #
60
+ # @api semipublic
61
+ #
62
+ def self.read(file)
63
+ parse(File.read(file))
64
+ end
65
+
66
+ #
67
+ # Loads the CVE JSON from the given file.
68
+ #
69
+ # @param [String] file
70
+ # The path to the file.
71
+ #
72
+ # @return [CVE]
73
+ # The loaded CVE object.
74
+ #
75
+ # @raise [InvalidJSON, MissingJSONKey, UnknownJSONValue]
76
+ # Failed to load the CVE JSON.
77
+ #
78
+ def self.load(file)
79
+ new(file, **from_json(read(file)))
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,80 @@
1
+ module CVEList
2
+ #
3
+ # Represents a directory within the {Repository}.
4
+ #
5
+ class Directory
6
+
7
+ # The path to the directory.
8
+ #
9
+ # @return [String]
10
+ attr_reader :path
11
+
12
+ #
13
+ # Initializes the directory.
14
+ #
15
+ # @param [String] path
16
+ # The path to the directory.
17
+ #
18
+ def initialize(path)
19
+ @path = File.expand_path(path)
20
+ end
21
+
22
+ #
23
+ # Joins the file/directory name(s) with the directory path.
24
+ #
25
+ # @param [Array<String>] names
26
+ # The file/directory name(s).
27
+ #
28
+ # @return [String]
29
+ #
30
+ def join(*names)
31
+ File.join(@path,*names)
32
+ end
33
+
34
+ #
35
+ # Determines whether the directory has the givne file.
36
+ #
37
+ # @param [String] name
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ def file?(name)
42
+ File.file?(join(name))
43
+ end
44
+
45
+ #
46
+ # Determines whether the directory has the given directory.
47
+ #
48
+ # @param [String] name
49
+ #
50
+ # @return [Boolean]
51
+ #
52
+ def directory?(name)
53
+ File.directory?(join(name))
54
+ end
55
+
56
+ #
57
+ # Finds all files and directories matching the pattern.
58
+ #
59
+ # @param [String] pattern
60
+ # The glob pattern.
61
+ #
62
+ # @return [Array<String>]
63
+ # The matching file and directory paths.
64
+ #
65
+ def glob(pattern)
66
+ Dir[join(pattern)]
67
+ end
68
+
69
+ #
70
+ # Converts the directory to a String.
71
+ #
72
+ # @return [String]
73
+ # The path to the directory.
74
+ #
75
+ def to_s
76
+ @path
77
+ end
78
+
79
+ end
80
+ end