cvelist 0.1.0

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.
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