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.
Files changed (43) hide show
  1. data/Gemfile +7 -0
  2. data/LICENSE +57 -0
  3. data/README.md +106 -0
  4. data/Rakefile +15 -0
  5. data/bin/fidius-cvedb +64 -0
  6. data/fidius-cvedb.gemspec +22 -0
  7. data/lib/cveparser/main.rb +31 -0
  8. data/lib/cveparser/ms_parser.rb +65 -0
  9. data/lib/cveparser/parser.rb +138 -0
  10. data/lib/cveparser/parser_model.rb +72 -0
  11. data/lib/cveparser/rails_store.rb +266 -0
  12. data/lib/db/migrate/20101122144313_create_impacts.rb +14 -0
  13. data/lib/db/migrate/20101122145008_create_default_impacts.rb +19 -0
  14. data/lib/db/migrate/20101122153216_create_cvsses.rb +19 -0
  15. data/lib/db/migrate/20101122174719_create_products.rb +19 -0
  16. data/lib/db/migrate/20101122175021_create_vulnerable_softwares.rb +16 -0
  17. data/lib/db/migrate/20101122175244_create_vulnerable_configurations.rb +14 -0
  18. data/lib/db/migrate/20101122175402_create_nvd_entries.rb +18 -0
  19. data/lib/db/migrate/20101125140254_create_vulnerability_references.rb +16 -0
  20. data/lib/db/migrate/20101202100411_create_xmls.rb +14 -0
  21. data/lib/db/migrate/20101210141850_create_mscves.rb +14 -0
  22. data/lib/db/migrate/20110118124541_change_impacts_structure.rb +13 -0
  23. data/lib/db/migrate/20110118131643_destroy_vulnerable_configurations.rb +13 -0
  24. data/lib/fidius-cvedb.rb +17 -0
  25. data/lib/fidius-cvedb/railtie.rb +14 -0
  26. data/lib/fidius-cvedb/version.rb +5 -0
  27. data/lib/models/fidius/cve_db/cve_connection.rb +7 -0
  28. data/lib/models/fidius/cve_db/cvss.rb +5 -0
  29. data/lib/models/fidius/cve_db/impact.rb +2 -0
  30. data/lib/models/fidius/cve_db/mscve.rb +3 -0
  31. data/lib/models/fidius/cve_db/nvd_entry.rb +23 -0
  32. data/lib/models/fidius/cve_db/product.rb +6 -0
  33. data/lib/models/fidius/cve_db/vulnerability_reference.rb +3 -0
  34. data/lib/models/fidius/cve_db/vulnerable_configuration.rb +6 -0
  35. data/lib/models/fidius/cve_db/vulnerable_software.rb +6 -0
  36. data/lib/models/fidius/cve_db/xml.rb +2 -0
  37. data/lib/tasks/db_backup.rake +30 -0
  38. data/lib/tasks/nvd_migrate.rake +25 -0
  39. data/lib/tasks/parse_cves.rake +146 -0
  40. data/test/cve_parser_test.rb +25 -0
  41. data/test/test_references.xml +9 -0
  42. data/test/test_v2.xml +3 -0
  43. 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,14 @@
1
+ class CreateImpacts < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :impacts do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ add_index :impacts, :name
9
+ end
10
+
11
+ def self.down
12
+ drop_table :impacts
13
+ end
14
+ 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