fidius-cvedb 0.0.2
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.
- data/Gemfile +7 -0
- data/LICENSE +57 -0
- data/README.md +106 -0
- data/Rakefile +15 -0
- data/bin/fidius-cvedb +64 -0
- data/fidius-cvedb.gemspec +22 -0
- data/lib/cveparser/main.rb +31 -0
- data/lib/cveparser/ms_parser.rb +65 -0
- data/lib/cveparser/parser.rb +138 -0
- data/lib/cveparser/parser_model.rb +72 -0
- data/lib/cveparser/rails_store.rb +266 -0
- data/lib/db/migrate/20101122144313_create_impacts.rb +14 -0
- data/lib/db/migrate/20101122145008_create_default_impacts.rb +19 -0
- data/lib/db/migrate/20101122153216_create_cvsses.rb +19 -0
- data/lib/db/migrate/20101122174719_create_products.rb +19 -0
- data/lib/db/migrate/20101122175021_create_vulnerable_softwares.rb +16 -0
- data/lib/db/migrate/20101122175244_create_vulnerable_configurations.rb +14 -0
- data/lib/db/migrate/20101122175402_create_nvd_entries.rb +18 -0
- data/lib/db/migrate/20101125140254_create_vulnerability_references.rb +16 -0
- data/lib/db/migrate/20101202100411_create_xmls.rb +14 -0
- data/lib/db/migrate/20101210141850_create_mscves.rb +14 -0
- data/lib/db/migrate/20110118124541_change_impacts_structure.rb +13 -0
- data/lib/db/migrate/20110118131643_destroy_vulnerable_configurations.rb +13 -0
- data/lib/fidius-cvedb.rb +17 -0
- data/lib/fidius-cvedb/railtie.rb +14 -0
- data/lib/fidius-cvedb/version.rb +5 -0
- data/lib/models/fidius/cve_db/cve_connection.rb +7 -0
- data/lib/models/fidius/cve_db/cvss.rb +5 -0
- data/lib/models/fidius/cve_db/impact.rb +2 -0
- data/lib/models/fidius/cve_db/mscve.rb +3 -0
- data/lib/models/fidius/cve_db/nvd_entry.rb +23 -0
- data/lib/models/fidius/cve_db/product.rb +6 -0
- data/lib/models/fidius/cve_db/vulnerability_reference.rb +3 -0
- data/lib/models/fidius/cve_db/vulnerable_configuration.rb +6 -0
- data/lib/models/fidius/cve_db/vulnerable_software.rb +6 -0
- data/lib/models/fidius/cve_db/xml.rb +2 -0
- data/lib/tasks/db_backup.rake +30 -0
- data/lib/tasks/nvd_migrate.rake +25 -0
- data/lib/tasks/parse_cves.rake +146 -0
- data/test/cve_parser_test.rb +25 -0
- data/test/test_references.xml +9 -0
- data/test/test_v2.xml +3 -0
- metadata +120 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
# Author:: FIDIUS (mailto:grp-fidius@tzi.de)
|
2
|
+
# License:: Distributes under the same terms as fidius-cvedb Gem
|
3
|
+
|
4
|
+
# This module provides the object model for one CVE entry as listed in
|
5
|
+
# the National Vulnerability Database (nvd.nist.gov).
|
6
|
+
|
7
|
+
module FIDIUS
|
8
|
+
module NVDParserModel
|
9
|
+
|
10
|
+
# Represents an entry from nvd.nist.gov
|
11
|
+
class NVDEntry
|
12
|
+
|
13
|
+
attr_accessor :cve, :vulnerable_configurations, :cvss, :vulnerable_software,
|
14
|
+
:published_datetime, :last_modified_datetime, :cwe, :summary,
|
15
|
+
:references
|
16
|
+
|
17
|
+
def initialize(params)
|
18
|
+
@vulnerable_configurations = params[:vulnerable_configurations]
|
19
|
+
@vulnerable_software = params[:vulnerable_software]
|
20
|
+
@published_datetime = params[:published_datetime]
|
21
|
+
@last_modified_datetime = params[:last_modified_datetime]
|
22
|
+
@cvss = params[:cvss]
|
23
|
+
@cve = params[:cve]
|
24
|
+
@cwe = params[:cwe]
|
25
|
+
@summary = params[:summary]
|
26
|
+
@references = params[:references]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# Contains all fields to represent a reference
|
32
|
+
class Reference
|
33
|
+
|
34
|
+
attr_accessor :source, :link, :name
|
35
|
+
|
36
|
+
def initialize(params)
|
37
|
+
@source = params[:source]
|
38
|
+
@link = params[:link]
|
39
|
+
@name = params[:name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"source=#{source}, link=#{link}, name=#{name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# Contains all fields of an CVSS-Score
|
49
|
+
# Underscore is needed because there is an rails model
|
50
|
+
# with the same name
|
51
|
+
class Cvss_
|
52
|
+
|
53
|
+
attr_accessor :score, :access_vector, :access_complexity, :authentication,
|
54
|
+
:confidentiality_impact, :integrity_impact, :availability_impact,
|
55
|
+
:source, :generated_on_datetime
|
56
|
+
|
57
|
+
def initialize(params)
|
58
|
+
@source = params[:source]
|
59
|
+
@score = params[:score]
|
60
|
+
@access_vector = params[:access_vector]
|
61
|
+
@authentication = params[:authentication]
|
62
|
+
@access_complexity = params[:access_complexity]
|
63
|
+
@integrity_impact = params[:integrity_impact]
|
64
|
+
@availability_impact = params[:availability_impact]
|
65
|
+
@confidentiality_impact = params[:confidentiality_impact]
|
66
|
+
@generated_on_datetime = params[:generated_on_datetime]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# Author:: FIDIUS (mailto:grp-fidius@tzi.de)
|
2
|
+
# License:: Distributes under the same terms as fidius-cvedb Gem
|
3
|
+
|
4
|
+
# This module stores CVE-Entries in a database. Therefore it makes use
|
5
|
+
# of the models provided with the gem and uses an ActiveRecord Connection
|
6
|
+
# to the database.
|
7
|
+
|
8
|
+
module FIDIUS::CveDb::RailsStore
|
9
|
+
|
10
|
+
MODIFIED_XML = "nvdcve-2.0-modified.xml"
|
11
|
+
|
12
|
+
# We temporarily store the vuln products in a hash to fix duplicates easily.
|
13
|
+
# The hash looks like this: { :"vulnerable_software_string" => [ cves ] }
|
14
|
+
@products = {}
|
15
|
+
|
16
|
+
# Takes an XML filename and entries parsed with FIDIUS::NVDParser before
|
17
|
+
# and stores the entries in the database.
|
18
|
+
def self.create_new_entries xml_file_name, entries
|
19
|
+
|
20
|
+
#Check, if XML-File was parsed before.
|
21
|
+
xml_db_entry = Xml.find_by_name(xml_file_name)
|
22
|
+
|
23
|
+
if xml_db_entry and xml_file_name != MODIFIED_XML
|
24
|
+
puts "\n#{xml_file_name} is already in the database! Please use 'rake nvd:update' to fetch the most recent updates."
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
start_time = Time.now
|
29
|
+
puts "[*] Storing the CVE-Entries in DB"
|
30
|
+
|
31
|
+
num_entries = entries.size
|
32
|
+
|
33
|
+
entries.each_with_index do |entry, index|
|
34
|
+
puts "Store: #{entry.cve} [#{index+1}/#{num_entries}]"
|
35
|
+
save_entry(entry, true)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Until now, the products are only remembered in the @products hash, they
|
39
|
+
# are saved when all products are collected so we dont have duplicates
|
40
|
+
save_products
|
41
|
+
|
42
|
+
params = {:name => xml_file_name, :create_time => Time.now.to_datetime}
|
43
|
+
|
44
|
+
if xml_db_entry
|
45
|
+
xml_db_entry.update_attributes(params[:create_time])
|
46
|
+
else
|
47
|
+
Xml.create(params)
|
48
|
+
end
|
49
|
+
|
50
|
+
total_time = (Time.now - start_time).round
|
51
|
+
puts "[*] All Entries & Products stored, this has taken "+
|
52
|
+
"#{total_time/60}:#{total_time%60}"
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Stores one entry in the database
|
57
|
+
# with_products_hash:
|
58
|
+
# true -> The products which belong to the entry are remembered in the
|
59
|
+
# globale "products"-hash instead of being stored in the db.
|
60
|
+
# This is used for initializing the database where we collect all
|
61
|
+
# products in one hash and store them afterwards instead of using
|
62
|
+
# Rails find_or_create_by_,,,
|
63
|
+
# false -> The products are stored with each product in the database.
|
64
|
+
def self.save_entry entry, with_products_hash
|
65
|
+
|
66
|
+
cvss_params = {}
|
67
|
+
if entry.cvss
|
68
|
+
cvss_params = cvss_hash(entry)
|
69
|
+
end
|
70
|
+
|
71
|
+
params = {
|
72
|
+
:cve => entry.cve,
|
73
|
+
:cwe => entry.cwe,
|
74
|
+
:summary => entry.summary,
|
75
|
+
:published => DateTime.xmlschema(entry.published_datetime),
|
76
|
+
:last_modified => DateTime.xmlschema(entry.last_modified_datetime),
|
77
|
+
:cvss => Cvss.create(cvss_params)
|
78
|
+
}
|
79
|
+
db_entry = NvdEntry.create(params)
|
80
|
+
|
81
|
+
entry.vulnerable_software.each do |product|
|
82
|
+
if with_products_hash # just remember it, we'll store it later.
|
83
|
+
if @products.has_key? product.to_sym
|
84
|
+
@products[product.to_sym] << entry.cve
|
85
|
+
else
|
86
|
+
@products[product.to_sym] = [ entry.cve ]
|
87
|
+
end
|
88
|
+
else
|
89
|
+
save_product product, entry.cve
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
create_references entry, db_entry.id
|
94
|
+
# HACK
|
95
|
+
begin
|
96
|
+
db_entry.save!
|
97
|
+
rescue
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# save_products does not check for product duplicates and should be used for
|
102
|
+
# the DB-initialization. fix_product_duplicates should be called afterwards.
|
103
|
+
def self.save_products
|
104
|
+
puts "[*] I'm storing the products now (#{@products.size})"
|
105
|
+
i = 0
|
106
|
+
@products.each_pair do |product, cves|
|
107
|
+
values = product.to_s.split(":")
|
108
|
+
values[1].sub!("/", "")
|
109
|
+
# values = [cpe, part, vendor, product, version, update, edition, language]
|
110
|
+
p = Product.create({
|
111
|
+
:part => values[1],
|
112
|
+
:vendor => values[2],
|
113
|
+
:product => values[3],
|
114
|
+
:version => values[4],
|
115
|
+
:update_nr => values[5],
|
116
|
+
:edition => values[6],
|
117
|
+
:language => values[7]
|
118
|
+
})
|
119
|
+
cves.each do |cve|
|
120
|
+
# Save the relations between vulnerable software and nvd_entries
|
121
|
+
VulnerableSoftware.find_or_create_by_nvd_entry_id_and_product_id(
|
122
|
+
NvdEntry.find_by_cve(cve).id, p.id)
|
123
|
+
end
|
124
|
+
if i % 100 == 0
|
125
|
+
puts "Stored 100 products [#{i}/#{@products.size}]"
|
126
|
+
end
|
127
|
+
i += 1
|
128
|
+
end
|
129
|
+
puts "[*] All products stored."
|
130
|
+
end
|
131
|
+
|
132
|
+
# Stores one product in the database
|
133
|
+
def self.save_product product, cve
|
134
|
+
values = product.to_s.split(":")
|
135
|
+
values[1].sub!("/", "")
|
136
|
+
p = Product.find_or_create_by_part_and_vendor_and_product_and_version_and_update_nr_and_edition_and_language(
|
137
|
+
values[1], values[2], values[3], values[4], values[5], values[6], values[7])
|
138
|
+
VulnerableSoftware.find_or_create_by_product_id_and_nvd_entry_id(p.id, NvdEntry.find_by_cve(cve).id)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Removes duplicates from the Products-table in the database
|
142
|
+
# Therefore it creates a hash and adds all products to it.
|
143
|
+
# This prevents duplicate entries and the non-unique products
|
144
|
+
# will be removed.
|
145
|
+
def self.fix_product_duplicates
|
146
|
+
products = Product.all
|
147
|
+
puts "[*] I'm checking #{products.size} products for duplicates."+
|
148
|
+
"Building a hash with unique products..."
|
149
|
+
cleaned_products = {}
|
150
|
+
products.each do |p|
|
151
|
+
product_name = ("#{p.part}:#{p.vendor}:#{p.product}:#{p.version}"+
|
152
|
+
":#{p.update_nr}:#{p.edition}:#{p.language}").to_sym
|
153
|
+
|
154
|
+
# There is another product which has the same content, so we need to
|
155
|
+
# change the vulnerable_software.product_id's
|
156
|
+
if cleaned_products.has_key? product_name
|
157
|
+
p.vulnerable_softwares.each do |vuln_s|
|
158
|
+
vuln_s.product_id = cleaned_products[product_name]
|
159
|
+
vuln_s.save!
|
160
|
+
end
|
161
|
+
|
162
|
+
# this is a newly found product so we remember its id
|
163
|
+
else
|
164
|
+
cleaned_products[product_name] = p.id
|
165
|
+
end
|
166
|
+
end
|
167
|
+
puts "[*] Hash complete. I'll delete all non-unique products now..."
|
168
|
+
# We now have a hash which has only unique products and destroy all other
|
169
|
+
# products
|
170
|
+
delete_count = 0
|
171
|
+
products.each do |product|
|
172
|
+
unless cleaned_products.has_value?(product.id)
|
173
|
+
puts "Duplicate ID=#{product.id}"
|
174
|
+
product.destroy
|
175
|
+
delete_count += 1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
puts "[*] I've deleted #{delete_count} duplicates."
|
179
|
+
end
|
180
|
+
|
181
|
+
# saves the references for an cve entry in the database
|
182
|
+
def self.create_references xml_entry, nvd_entry_id
|
183
|
+
xml_entry.references.each do |ref|
|
184
|
+
VulnerabilityReference.create({
|
185
|
+
:name => ref.name,
|
186
|
+
:link => ref.link,
|
187
|
+
:source => ref.source,
|
188
|
+
:nvd_entry_id => nvd_entry_id
|
189
|
+
})
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# In contrast to save_entries_to_models this method checks already existent
|
194
|
+
# CVE Entries. Therefore the former should be used to initialize the CVE-DB
|
195
|
+
# and update_cves to store newly or updated CVE-Entries.
|
196
|
+
def self.update_cves xml_entries
|
197
|
+
i_new = 0
|
198
|
+
i_updated = 0
|
199
|
+
puts "[*] Updating or creating NVD Entries."
|
200
|
+
xml_entries.each_with_index do |xml_entry, index|
|
201
|
+
|
202
|
+
entry_params = {
|
203
|
+
:cwe => xml_entry.cwe,
|
204
|
+
:summary => xml_entry.summary,
|
205
|
+
:published => xml_entry.published_datetime,
|
206
|
+
:last_modified => xml_entry.last_modified_datetime
|
207
|
+
}
|
208
|
+
|
209
|
+
if nvd_entry = NvdEntry.find_by_cve(xml_entry.cve) # update entry
|
210
|
+
nvd_entry.update_attributes(entry_params)
|
211
|
+
|
212
|
+
|
213
|
+
xml_entry.vulnerable_software.each do |xml_product|
|
214
|
+
values = xml_product.to_s.split(":")
|
215
|
+
values[1].sub!("/", "")
|
216
|
+
product = Product.find_or_initialize_by_part_and_vendor_and_product_and_version_and_update_nr_and_edition_and_language({
|
217
|
+
:part => values[1],
|
218
|
+
:vendor => values[2],
|
219
|
+
:product => values[3],
|
220
|
+
:version => values[4],
|
221
|
+
:update_nr => values[5],
|
222
|
+
:edition => values[6],
|
223
|
+
:language => values[7]
|
224
|
+
})
|
225
|
+
|
226
|
+
if product.new_record?
|
227
|
+
VulnerableSoftware.find_or_create_by_nvd_entry_id_and_product_id(nvd_entry.id, product.id)
|
228
|
+
product.save!
|
229
|
+
end
|
230
|
+
|
231
|
+
if nvd_entry.cvss
|
232
|
+
nvd_entry.cvss.update_attributes(cvss_hash xml_entry)
|
233
|
+
else
|
234
|
+
nvd_entry.cvss = Cvss.create(cvss_hash xml_entry)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
nvd_entry.vulnerability_references.destroy_all
|
239
|
+
create_references xml_entry, nvd_entry.id
|
240
|
+
|
241
|
+
nvd_entry.save!
|
242
|
+
i_updated += 1
|
243
|
+
else
|
244
|
+
save_entry xml_entry, false
|
245
|
+
i_new += 1
|
246
|
+
end
|
247
|
+
puts "[#{index+1}/#{xml_entries.size}]"
|
248
|
+
end
|
249
|
+
puts "[*] #{i_new} Entries created, #{i_updated} updated."
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns a hash filled with CVSS-data from the database
|
253
|
+
def self.cvss_hash entry
|
254
|
+
cvss_params = {
|
255
|
+
:score => entry.cvss.score,
|
256
|
+
:source => entry.cvss.source,
|
257
|
+
:generated_on => DateTime.xmlschema(entry.cvss.generated_on_datetime),
|
258
|
+
:access_vector => entry.cvss.access_vector,
|
259
|
+
:access_complexity => entry.cvss.access_complexity,
|
260
|
+
:authentication => entry.cvss.authentication,
|
261
|
+
:confidentiality_impact_id => Impact.find_by_name(entry.cvss.confidentiality_impact).id,
|
262
|
+
:integrity_impact_id => Impact.find_by_name(entry.cvss.integrity_impact).id,
|
263
|
+
:availability_impact_id => Impact.find_by_name(entry.cvss.availability_impact).id
|
264
|
+
}
|
265
|
+
end
|
266
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateDefaultImpacts < ActiveRecord::Migration
|
2
|
+
|
3
|
+
IMPACT_DEFAULTS = %w[NONE PARTIAL COMPLETE]
|
4
|
+
|
5
|
+
def self.up
|
6
|
+
IMPACT_DEFAULTS.each do |name|
|
7
|
+
FIDIUS::CveDb::Impact.find_or_create_by_name(name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.down
|
12
|
+
IMPACT_DEFAULTS.each do |name|
|
13
|
+
impacts = FIDIUS::CveDb::Impact.where({ :name => name })
|
14
|
+
impacts.each do |impact|
|
15
|
+
impact.destroy!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateCvsses < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :cvsses do |t|
|
4
|
+
t.float :score
|
5
|
+
t.string :source
|
6
|
+
t.datetime :generated_on
|
7
|
+
t.string :access_vector
|
8
|
+
t.string :access_complexity
|
9
|
+
t.string :authentication
|
10
|
+
t.integer :nvd_entry_id
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :cvsses
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateProducts < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :products do |t|
|
4
|
+
t.string :part
|
5
|
+
t.string :vendor
|
6
|
+
t.string :product
|
7
|
+
t.string :version
|
8
|
+
t.string :update_nr
|
9
|
+
t.string :edition
|
10
|
+
t.string :language
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :products
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateVulnerableSoftwares < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :vulnerable_softwares do |t|
|
4
|
+
t.integer :nvd_entry_id
|
5
|
+
t.integer :product_id
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
add_index :vulnerable_softwares, :nvd_entry_id
|
10
|
+
add_index :vulnerable_softwares, :product_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :vulnerable_softwares
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateVulnerableConfigurations < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :vulnerable_configurations do |t|
|
4
|
+
t.integer :nvd_entry_id
|
5
|
+
t.integer :product_id
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.down
|
12
|
+
drop_table :vulnerable_configurations
|
13
|
+
end
|
14
|
+
end
|