vrt 0.1
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.
- checksums.yaml +7 -0
- data/lib/data/1.0/vrt.schema.json +62 -0
- data/lib/data/1.0/vulnerability-rating-taxonomy.json +1526 -0
- data/lib/data/1.1/deprecated-node-mapping.json +8 -0
- data/lib/data/1.1/vrt.schema.json +62 -0
- data/lib/data/1.1/vulnerability-rating-taxonomy.json +1559 -0
- data/lib/generators/vrt.rb +3 -0
- data/lib/generators/vrt/install_generator.rb +12 -0
- data/lib/vrt.rb +123 -0
- data/lib/vrt/cross_version_mapping.rb +45 -0
- data/lib/vrt/map.rb +82 -0
- data/lib/vrt/node.rb +46 -0
- data/lib/vrt/version.rb +3 -0
- metadata +116 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module Vrt
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root(File.expand_path(File.dirname(__FILE__)))
|
7
|
+
def create_initializer_file
|
8
|
+
copy_file '../vrt.rb', 'config/initializers/vrt.rb'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/vrt.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# The 'guts' of the VRT library. Probably in need of refactoring in future.
|
2
|
+
# Will do file I/O the first time it's accessed, and will thereafter hold
|
3
|
+
# VRT versions in-memory.
|
4
|
+
|
5
|
+
require 'vrt/map'
|
6
|
+
require 'vrt/node'
|
7
|
+
require 'vrt/cross_version_mapping'
|
8
|
+
|
9
|
+
require 'date'
|
10
|
+
require 'json'
|
11
|
+
require 'pathname'
|
12
|
+
|
13
|
+
module VRT
|
14
|
+
DIR = Pathname.new(__dir__).join('data')
|
15
|
+
OTHER_OPTION = { 'id' => 'other',
|
16
|
+
'name' => 'Other',
|
17
|
+
'priority' => nil,
|
18
|
+
'type' => 'category' }.freeze
|
19
|
+
|
20
|
+
@version_json = {}
|
21
|
+
@last_update = {}
|
22
|
+
|
23
|
+
module_function
|
24
|
+
|
25
|
+
extend CrossVersionMapping
|
26
|
+
|
27
|
+
# Infer the available versions of the VRT from the names of the files
|
28
|
+
# in the repo.
|
29
|
+
# The returned list is in semver order with the current version first.
|
30
|
+
def versions
|
31
|
+
@versions ||= json_dir_names.sort_by { |v| Gem::Version.new(v) }.reverse!
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get the most recent version of the VRT.
|
35
|
+
def current_version
|
36
|
+
versions.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_version?(version)
|
40
|
+
version == current_version
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get the last updated timestamp of the VRT data (not schema!)
|
44
|
+
# Passing nil for version will return the latest version.
|
45
|
+
def last_updated(version = nil)
|
46
|
+
version ||= current_version
|
47
|
+
return @last_update[version] if @last_update[version]
|
48
|
+
metadata = JSON.parse(json_pathname(version).read)['metadata']
|
49
|
+
@last_update[version] = Date.parse(metadata['release_date'])
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_categories
|
53
|
+
Map.new(current_version).categories
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get all deprecated ids that would match in the given categories from the current version
|
57
|
+
def all_matching_categories(categories)
|
58
|
+
cross_version_category_mapping.select { |key, _value| categories.include?(key) }.values.flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
# Finds the best match valid node. First looks at valid nodes in the given new version or finds
|
62
|
+
# the appropriate deprecated mapping. If neither is found it will walk up the tree to find a
|
63
|
+
# valid parent node before giving up and returning nil.
|
64
|
+
#
|
65
|
+
# @param [String] A valid vrt_id
|
66
|
+
# @param [String] (Optional - recommended) A valid vrt_version that the vrt_id exists in
|
67
|
+
# @param [string] (Optional) The preferred new vrt_version to find a match in
|
68
|
+
# @param [String] (Optional) The maximum depth to match in
|
69
|
+
# @return [VRT::Node|Nil] A valid VRT::Node object or nil if no best match could be found
|
70
|
+
def find_node(vrt_id:, version: nil, preferred_version: nil, max_depth: 'variant')
|
71
|
+
new_version = preferred_version || current_version
|
72
|
+
if Map.new(new_version).valid?(vrt_id)
|
73
|
+
Map.new(new_version).find_node(vrt_id, max_depth: max_depth)
|
74
|
+
elsif deprecated_node?(vrt_id)
|
75
|
+
find_deprecated_node(vrt_id, preferred_version, max_depth)
|
76
|
+
else
|
77
|
+
return nil unless version
|
78
|
+
find_valid_parent_node(vrt_id, version, new_version, max_depth)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Load the VRT from text files, and parse it as JSON.
|
83
|
+
# If other: true, we append the OTHER_OPTION hash at runtime (not cached)
|
84
|
+
def get_json(version: nil, other: true)
|
85
|
+
version ||= current_version
|
86
|
+
@version_json[version] ||= json_for_version(version)
|
87
|
+
other ? @version_json[version] + [OTHER_OPTION] : @version_json[version]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get names of directories matching lib/data/<major>-<minor>/
|
91
|
+
def json_dir_names
|
92
|
+
DIR.entries
|
93
|
+
.map(&:basename)
|
94
|
+
.map(&:to_s)
|
95
|
+
.select { |dirname| dirname =~ /^[0-9]+\.[0-9]/ }.sort
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get the Pathname for a particular version
|
99
|
+
def json_pathname(version)
|
100
|
+
DIR.join(version, 'vulnerability-rating-taxonomy.json')
|
101
|
+
end
|
102
|
+
|
103
|
+
# Load and parse JSON for some VRT version
|
104
|
+
def json_for_version(version)
|
105
|
+
JSON.parse(json_pathname(version).read)['content']
|
106
|
+
end
|
107
|
+
|
108
|
+
# Cache the VRT contents in-memory, so we're not hitting File I/O multiple times per
|
109
|
+
# request that needs it.
|
110
|
+
def reload!
|
111
|
+
unload!
|
112
|
+
versions
|
113
|
+
get_json
|
114
|
+
last_updated
|
115
|
+
end
|
116
|
+
|
117
|
+
# We separate unload! out, as we need to call it in test environments.
|
118
|
+
def unload!
|
119
|
+
@versions = nil
|
120
|
+
@version_json = {}
|
121
|
+
@last_update = {}
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module VRT
|
2
|
+
module CrossVersionMapping
|
3
|
+
# Maps new_category_id: deprecated_node_id
|
4
|
+
def cross_version_category_mapping
|
5
|
+
category_map = {}
|
6
|
+
deprecated_node_json.each do |key, value|
|
7
|
+
latest_version = value.keys.sort_by { |n| Gem::Version.new(n) }.last
|
8
|
+
id = value[latest_version].split('.')[0]
|
9
|
+
category_map[id] ? category_map[id] << key : category_map[id] = [key]
|
10
|
+
end
|
11
|
+
category_map
|
12
|
+
end
|
13
|
+
|
14
|
+
# Map shape: { deprecated_id: { version: new_mapped_id } }
|
15
|
+
def deprecated_node_json
|
16
|
+
filename = VRT::DIR.join(current_version, 'deprecated-node-mapping.json')
|
17
|
+
File.file?(filename) ? JSON.parse(File.read(filename)) : {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def deprecated_node?(vrt_id)
|
21
|
+
deprecated_node_json[vrt_id]
|
22
|
+
end
|
23
|
+
|
24
|
+
def latest_version_for_deprecated_node(vrt_id)
|
25
|
+
deprecated_node_json[vrt_id].keys.sort_by { |n| Gem::Version.new(n) }.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_deprecated_node(vrt_id, new_version = nil, max_depth = 'variant')
|
29
|
+
version = latest_version_for_deprecated_node(vrt_id)
|
30
|
+
node_id = deprecated_node_json[vrt_id][new_version] || deprecated_node_json[vrt_id][version]
|
31
|
+
VRT::Map.new(new_version).find_node(node_id, max_depth: max_depth)
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_valid_parent_node(vrt_id, old_version, new_version, max_depth)
|
35
|
+
old_node = VRT::Map.new(old_version).find_node(vrt_id)
|
36
|
+
new_map = VRT::Map.new(new_version)
|
37
|
+
if new_map.valid?(vrt_id)
|
38
|
+
new_map.find_node(vrt_id, max_depth: max_depth)
|
39
|
+
else
|
40
|
+
return nil if old_node.parent.nil?
|
41
|
+
find_valid_parent_node(old_node.parent.qualified_vrt_id, old_version, new_version, max_depth)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/vrt/map.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module VRT
|
2
|
+
class Map
|
3
|
+
DEPTH_MAP = {
|
4
|
+
'category' => 1,
|
5
|
+
'subcategory' => 2,
|
6
|
+
'variant' => 3
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
attr_reader :structure, :version
|
10
|
+
|
11
|
+
def initialize(version = nil)
|
12
|
+
@version = version || VRT.current_version
|
13
|
+
@structure = build_structure
|
14
|
+
@found_nodes = {}
|
15
|
+
@lineages = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_node(string, max_depth: 'variant')
|
19
|
+
@found_nodes[string + max_depth] ||= walk_node_tree(string, max_depth: max_depth)
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?(node)
|
23
|
+
return false unless node =~ /[[:lower]]/
|
24
|
+
node == 'other' || find_node(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_lineage(string, max_depth: 'variant')
|
28
|
+
@lineages[string] ||= construct_lineage(string, max_depth)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns list of top level categories in the shape:
|
32
|
+
# [{ value: category_id, label: category_name }]
|
33
|
+
def categories
|
34
|
+
structure.keys.map do |key|
|
35
|
+
node = find_node(key.to_s, max_depth: 'category')
|
36
|
+
{ value: node.id, label: node.name }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def construct_lineage(string, max_depth)
|
43
|
+
lineage = ''
|
44
|
+
walk_node_tree(string, max_depth: max_depth) do |ids, node, level|
|
45
|
+
lineage += node.name
|
46
|
+
lineage += ' > ' unless level == ids.length
|
47
|
+
end
|
48
|
+
lineage
|
49
|
+
end
|
50
|
+
|
51
|
+
def walk_node_tree(string, max_depth: 'variant')
|
52
|
+
id_tokens = string.split('.').map(&:to_sym)
|
53
|
+
return nil if id_tokens.size > 3
|
54
|
+
ids = id_tokens.take(DEPTH_MAP[max_depth])
|
55
|
+
node = @structure[ids[0]]
|
56
|
+
ids.each_index do |idx|
|
57
|
+
level = idx + 1
|
58
|
+
yield(ids, node, level) if block_given?
|
59
|
+
node = search(ids, node, level)
|
60
|
+
end
|
61
|
+
node
|
62
|
+
end
|
63
|
+
|
64
|
+
def search(ids, node, level)
|
65
|
+
last_level = level.eql?(ids.length)
|
66
|
+
last_level ? node : node&.children&.fetch(ids[level], false)
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_structure
|
70
|
+
VRT.get_json(version: @version).reduce({}, &method(:build_node))
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_node(memo, vrt, parent = nil)
|
74
|
+
node = Node.new(vrt.merge('version' => @version, 'parent' => parent))
|
75
|
+
if node.children?
|
76
|
+
node.children = vrt['children'].reduce({}) { |m, v| build_node(m, v, node) }
|
77
|
+
end
|
78
|
+
memo[node.id] = node
|
79
|
+
memo
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/vrt/node.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module VRT
|
2
|
+
class Node
|
3
|
+
attr_reader :id, :name, :priority, :type, :version, :parent, :qualified_vrt_id
|
4
|
+
attr_accessor :children
|
5
|
+
|
6
|
+
def initialize(attributes = {})
|
7
|
+
@id = attributes['id'].to_sym
|
8
|
+
@name = attributes['name']
|
9
|
+
@priority = attributes['priority']
|
10
|
+
@type = attributes['type']
|
11
|
+
@has_children = attributes.key?('children')
|
12
|
+
@children = {}
|
13
|
+
@version = attributes['version']
|
14
|
+
@parent = attributes['parent']
|
15
|
+
@qualified_vrt_id = construct_vrt_id
|
16
|
+
end
|
17
|
+
|
18
|
+
def children?
|
19
|
+
@has_children
|
20
|
+
end
|
21
|
+
|
22
|
+
def construct_vrt_id
|
23
|
+
parent ? "#{parent.qualified_vrt_id}.#{id}" : id.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# Since this object contains references to parent and children,
|
27
|
+
# as_json must be overridden to avoid unending recursion.
|
28
|
+
def as_json(options = nil)
|
29
|
+
json = {}
|
30
|
+
instance_variables.each do |attribute|
|
31
|
+
attr_name = attribute.to_s.tr('@', '')
|
32
|
+
json[attr_name] = case attr_name
|
33
|
+
when 'parent'
|
34
|
+
parent&.qualified_vrt_id
|
35
|
+
when 'children'
|
36
|
+
children.inject({}) do |c, (k, v)|
|
37
|
+
c[k] = v.nil? ? v : v.as_json(options)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
instance_variable_get(attribute)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
json
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/vrt/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vrt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Barnett Klane
|
8
|
+
- Max Schwenk
|
9
|
+
- Adam David
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2017-07-21 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.14'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.14'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rake
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: rspec
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rubocop
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0.48'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0.48'
|
71
|
+
description:
|
72
|
+
email:
|
73
|
+
- barnett@bugcrowd.com
|
74
|
+
- max.schwenk@bugcrowd.com
|
75
|
+
- adam.david@bugcrowd.com
|
76
|
+
executables: []
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- lib/data/1.0/vrt.schema.json
|
81
|
+
- lib/data/1.0/vulnerability-rating-taxonomy.json
|
82
|
+
- lib/data/1.1/deprecated-node-mapping.json
|
83
|
+
- lib/data/1.1/vrt.schema.json
|
84
|
+
- lib/data/1.1/vulnerability-rating-taxonomy.json
|
85
|
+
- lib/generators/vrt.rb
|
86
|
+
- lib/generators/vrt/install_generator.rb
|
87
|
+
- lib/vrt.rb
|
88
|
+
- lib/vrt/cross_version_mapping.rb
|
89
|
+
- lib/vrt/map.rb
|
90
|
+
- lib/vrt/node.rb
|
91
|
+
- lib/vrt/version.rb
|
92
|
+
homepage: http://rubygems.org/gems/vrt
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.6.12
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: Ruby wrapper for Bugcrowd's Vulnerability Rating Taxonomy
|
116
|
+
test_files: []
|