File Coverage

blib/lib/NIST/NVD/Store/SQLite3.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package NIST::NVD::Store::SQLite3;
2              
3 1     1   35020 use strict;
  1         3  
  1         42  
4 1     1   7 use warnings;
  1         2  
  1         32  
5              
6 1     1   645 use NIST::NVD::Store::Base;
  0            
  0            
7              
8             use base qw{NIST::NVD::Store::Base};
9              
10             our @ISA;
11             push @ISA, qw{NIST::NVD::Store::Base};
12              
13             use Carp(qw(carp confess cluck));
14              
15             use Storable qw(nfreeze thaw);
16             use DBI;
17             use Time::HiRes qw( gettimeofday );
18              
19             =head1 NAME
20              
21             NIST::NVD::Store::SQLite3 - SQLite3 store for NIST::NVD
22              
23             =head1 VERSION
24              
25             Version 1.00.00
26              
27             =cut
28              
29             our $VERSION = '1.00.00';
30              
31             my %query = (
32             cpe_create => qq{
33             CREATE TABLE IF NOT EXISTS cpe (
34             id INTEGER PRIMARY KEY AUTOINCREMENT,
35             urn VARCHAR(64) CONSTRAINT uniq_urn UNIQUE ON CONFLICT FAIL,
36              
37             part CHAR,
38             vendor VARCHAR(16),
39             product VARCHAR(16),
40             version VARCHAR(16),
41             updt VARCHAR(16),
42             edition VARCHAR(16),
43             language VARCHAR(4)
44             )},
45             cve_create => qq{
46             CREATE TABLE IF NOT EXISTS cve (
47             id INTEGER PRIMARY KEY AUTOINCREMENT,
48             score REAL,
49             cve_id VARCHAR(16) CONSTRAINT uniq_cve_id UNIQUE ON CONFLICT FAIL,
50             cve_dump BLOB
51             )},
52             cwe_create => qq{
53             CREATE TABLE IF NOT EXISTS cwe (
54             id INTEGER PRIMARY KEY AUTOINCREMENT,
55             cwe_id VARCHAR(16) CONSTRAINT uniq_cwe_id UNIQUE ON CONFLICT FAIL,
56             cwe_dump BLOB
57             )},
58             cpe_cve_map_create => qq{
59             CREATE TABLE IF NOT EXISTS cpe_cve_map (
60             id INTEGER PRIMARY KEY AUTOINCREMENT,
61              
62             cpe_id INTEGER,
63             cve_id INTEGER,
64             CONSTRAINT uniq_cpe_cve UNIQUE ( cpe_id, cve_id ) ON CONFLICT FAIL
65             )},
66             cpe_cwe_map_create => qq{
67             CREATE TABLE IF NOT EXISTS cpe_cwe_map (
68             id INTEGER PRIMARY KEY AUTOINCREMENT,
69              
70             cpe_id INTEGER,
71             cwe_id INTEGER,
72             CONSTRAINT uniq_cpe_cwe UNIQUE ( cpe_id, cwe_id ) ON CONFLICT IGNORE
73             )},
74             cve_cwe_map_create => qq{
75             CREATE TABLE IF NOT EXISTS cve_cwe_map (
76             id INTEGER PRIMARY KEY AUTOINCREMENT,
77              
78             cve_id INTEGER,
79             cwe_id VARCHAR(64),
80             CONSTRAINT uniq_cve_cwe UNIQUE ( cve_id, cwe_id ) ON CONFLICT IGNORE
81             )},
82             websec_create => qq{
83             CREATE TABLE IF NOT EXISTS cpe_websec_score (
84             id INTEGER PRIMARY KEY AUTOINCREMENT,
85              
86             cpe_urn VARCHAR(64) UNIQUE,
87             cat_a0 REAL,
88             cat_a1 REAL,
89             cat_a2 REAL,
90             cat_a3 REAL,
91             cat_a4 REAL,
92             cat_a5 REAL,
93             cat_a6 REAL,
94             cat_a7 REAL,
95             cat_a8 REAL,
96             cat_a9 REAL,
97             cat_a10 REAL
98              
99             )},
100             verify_cwe_pkid => qq{
101             SELECT cwe_id,id FROM cwe WHERE id=?
102             },
103             cve_for_cpe_select => qq{
104             SELECT cve.cve_id
105             FROM cpe_cve_map,cve
106             WHERE cpe_cve_map.cpe_id=?
107             AND cpe_cve_map.cve_id=cve.id
108             ORDER BY cve.cve_id
109             },
110             cwe_for_cpe_select => qq{
111             SELECT cwe.cwe_id
112             FROM cpe_cwe_map,cwe
113             WHERE cpe_cwe_map.cpe_id=?
114             AND cpe_cwe_map.cwe_id=cwe.id
115             ORDER BY cwe.cwe_id
116             },
117             get_cpe_id_select => qq{
118             SELECT id FROM cpe WHERE cpe.urn=?
119             },
120             get_cve_id_select => qq{
121             SELECT id FROM cve WHERE cve.cve_id=?
122             },
123             get_cwe_id_select => qq{
124             SELECT id FROM cwe WHERE cwe.cwe_id=?
125             },
126             get_cve_select => qq{
127             SELECT cve_dump FROM cve WHERE cve.cve_id=?
128             },
129             get_websec_score_select_by_cpe => qq{
130             SELECT cat_a0, cat_a1, cat_a2, cat_a3, cat_a4, cat_a5, cat_a6, cat_a7,
131             cat_a8, cat_a9, cat_a10 FROM cpe_websec_score WHERE cpe_urn=?
132             },
133             get_cve_id_select_by_pkey => qq{
134             SELECT id,cve_id FROM cve WHERE id=?
135             },
136             get_cve_id_select_by_friendly => qq{
137             SELECT id,cve_id FROM cve WHERE cve_id=?
138             },
139             get_cwe_select => qq{
140             SELECT id,cwe_dump FROM cwe WHERE cwe.cwe_id=?
141             },
142             put_cve_idx_cpe_insert => qq{
143             INSERT INTO cpe_cve_map (cpe_id,cve_id)
144             VALUES ( ?, ? )
145             },
146             put_cwe_idx_cpe_insert => qq{
147             INSERT INTO cpe_cwe_map (cpe_id,cwe_id)
148             VALUES ( ?, ? )
149             },
150             put_cwe_idx_cve_insert => qq{
151             INSERT INTO cve_cwe_map (cve_id,cwe_id)
152             VALUES ( ?, ? )
153             },
154             put_cve_insert => qq{
155             INSERT INTO cve ( cve_dump, score, cve_id ) VALUES (?, ?, ?)
156             },
157             put_cve_update => qq{
158             UPDATE cve SET cve_dump=?, score=? WHERE cve.id=?
159             },
160             put_cwe_insert => qq{
161             INSERT INTO cwe ( cwe_dump, cwe_id ) VALUES (?, ?)
162             },
163             put_cwe_update => qq{
164             UPDATE cwe SET cwe_dump=? WHERE cwe.id=?
165             },
166             put_cpe_insert => qq{
167             INSERT INTO cpe ( urn,part,vendor,product,version,updt,edition,language )
168             VALUES( ?,?,?,?,?,?,?,? )
169             }
170              
171             );
172              
173             my %sth = ();
174              
175             =head1 SYNOPSIS
176              
177             $q =
178             eval { NIST::NVD::Query->new( store => 'SQLite3', database => $db_file, ); };
179              
180              
181             =head1 SUBROUTINES/METHODS
182              
183             =head2 new
184              
185             my $NVD_Storage_SQLite3 = NIST::NVD::Store::SQLite3->new(
186             store => 'SQLite3',
187             database => '/path/to/database.sqlite',
188             );
189              
190             =cut
191              
192             sub new {
193             my ( $class, %args ) = @_;
194             $class = ref $class || $class;
195              
196             # my $self = $class->SUPER::new(%args);
197              
198             my $self = bless { store => $args{store} }, $class;
199              
200             my $store = $args{store};
201              
202             unless ( exists $args{database} && $args{database} ) {
203             confess('database argument is required, but was not passed');
204             return;
205             }
206              
207             $self->{$store} = $self->_connect_db( database => $args{database} );
208              
209             $self->{sqlite} = $self->{SQLite3};
210              
211             $self->{vuln_software} = {};
212              
213             foreach my $statement (
214             qw( put_cpe_insert
215             cve_for_cpe_select cwe_for_cpe_select
216             put_cve_idx_cpe_insert
217             put_cwe_idx_cpe_insert
218             put_cwe_idx_cve_insert
219             put_cve_insert put_cve_update
220             put_cwe_insert put_cwe_update
221             get_cpe_id_select
222             get_cve_id_select get_cve_select
223             get_cwe_id_select get_cwe_select
224             get_cve_id_select_by_pkey
225             get_cve_id_select_by_friendly
226             get_websec_score_select_by_cpe
227             )
228             )
229             {
230             $sth{$statement} = $self->{sqlite}->prepare( $query{$statement} )
231             or die "couldn't prepare statement '$statement'";
232             }
233             my $fail = 0;
234              
235             return if $fail;
236              
237             return $self;
238             }
239              
240             sub _connect_db {
241             my ( $self, %args ) = @_;
242              
243             my $dbh = DBI->connect( "dbi:SQLite:dbname=$args{database}", "", "" );
244              
245             foreach my $statement (
246             qw(
247             cpe_create websec_create
248             cve_cwe_map_create
249             cve_create cpe_cve_map_create
250             cwe_create cpe_cwe_map_create
251             verify_cwe_pkid
252             )
253             )
254             {
255              
256             my $query = $query{$statement};
257              
258             $sth{$statement} //= $dbh->prepare($query);
259             $sth{$statement}->execute();
260             }
261              
262             return $dbh;
263             }
264              
265             =head2 get_cve_for_cpe
266              
267             =cut
268              
269             my $cpe_urn_re = qr{^(cpe:/(.)(:[^:]+){2,6})$};
270              
271             sub get_cve_for_cpe {
272             my ( $self, %args ) = @_;
273              
274             my $cpe = $args{cpe};
275              
276             return unless $cpe;
277              
278             my $cpe_pkey_id;
279             if ( $cpe =~ /^\d+$/ ) {
280             $cpe_pkey_id = $cpe;
281             }
282             else {
283             ( my ( $cpe, @parts ) ) = ( $args{cpe} =~ $cpe_urn_re );
284             $cpe_pkey_id = $self->_get_cpe_id($cpe);
285             }
286              
287             $sth{cve_for_cpe_select}->execute($cpe_pkey_id);
288              
289             my $cve_id = [];
290              
291             while ( my $row = $sth{cve_for_cpe_select}->fetchrow_hashref() ) {
292             push( @$cve_id, $row->{'cve_id'} );
293             }
294              
295             return $cve_id;
296             }
297              
298             =head2 get_cwe_for_cpe
299              
300             =cut
301              
302             sub get_cwe_for_cpe {
303             my ( $self, %args ) = @_;
304              
305             my $cpe = $args{cpe};
306              
307             return unless $cpe;
308              
309             my $cpe_pkey_id;
310             if ( $cpe =~ /^\d+$/ ) {
311             $cpe_pkey_id = $cpe;
312             }
313             else {
314             ( my ( $cpe, @parts ) ) = ( $args{cpe} =~ $cpe_urn_re );
315             $cpe_pkey_id = $self->_get_cpe_id($cpe);
316             }
317              
318             $sth{cwe_for_cpe_select}->execute($cpe_pkey_id);
319              
320             my $cwe_id = [];
321              
322             while ( my $row = $sth{cwe_for_cpe_select}->fetchrow_hashref() ) {
323             push( @$cwe_id, $row->{'cwe_id'} );
324             }
325              
326             return $cwe_id;
327             }
328              
329             my %cve_id_cache;
330              
331             sub _get_cve_id {
332             my ( $self, $cve_id ) = @_;
333              
334             return @{ $cve_id_cache{$cve_id} } if exists $cve_id_cache{$cve_id};
335              
336             my $sth;
337             my $query;
338              
339             if ( $cve_id =~ /^\d+$/ ) {
340             $sth = $sth{get_cve_id_select_by_pkey};
341             }
342             elsif ( $cve_id =~ /^CVE-\d+-\d+/ ) {
343             $sth = $sth{get_cve_id_select_by_friendly};
344             }
345             else {
346             confess "cve id malformed\n";
347             die;
348             }
349              
350             $sth->execute($cve_id);
351             my $row = $sth->fetchrow_hashref();
352             return unless $row;
353              
354             $cve_id_cache{$cve_id} = [ $row->{id}, $row->{cve_id} ];
355              
356             return ( $row->{id}, $row->{cve_id} );
357             }
358              
359             my %cwe_id_cache;
360              
361             sub _get_cwe_id {
362             my ( $self, $cwe_id ) = @_;
363              
364             return @{ $cwe_id_cache{$cwe_id} } if exists $cwe_id_cache{$cwe_id};
365              
366             my $sth;
367              
368             if ( $cwe_id =~ /^\d+$/ ) {
369             $sth = $self->{sqlite}->prepare('SELECT id,cwe_id FROM cwe WHERE id=?');
370              
371             }
372             elsif ( $cwe_id =~ /^CWE-\d+/ ) {
373              
374             $sth =
375             $self->{sqlite}->prepare('SELECT id,cwe_id FROM cwe WHERE cwe_id=?');
376             }
377             else {
378             cluck "cwe id malformed\n";
379             return;
380             }
381              
382             $sth->execute($cwe_id);
383             my $row = $sth->fetchrow_hashref();
384              
385             unless ($row) {
386             return;
387             }
388              
389             $cwe_id_cache{$cwe_id} = [ $row->{qw{id cwe_id}} ];
390             \
391              
392             return ( $row->{qw{id cwe_id}} );
393             }
394              
395             sub _get_cpe_id {
396             my ( $self, $cpe_urn ) = @_;
397              
398             return $self->{cpe_map}->{$cpe_urn}
399             if ( exists $self->{cpe_map}->{$cpe_urn} );
400              
401             $sth{get_cpe_id_select}->execute($cpe_urn);
402              
403             # TODO: Assert that this query only returns one result
404             my $rows = 0;
405             while ( my $row = $sth{get_cpe_id_select}->fetchrow_hashref() ) {
406             carp
407             "multiple ($rows) results for value intended to be unique. cpe_urn: [$cpe_urn]\n"
408             if ( $rows != 0 );
409             $self->{cpe_map}->{$cpe_urn} = $row->{id};
410             }
411              
412             return $self->{cpe_map}->{$cpe_urn};
413             }
414              
415             sub _get_query {
416             my ( $self, $query_name ) = @_;
417              
418             return $query{$query_name}
419             if ($query_name);
420              
421             return %query if wantarray;
422              
423             return \%query;
424             }
425              
426             sub _get_sth {
427             my ( $self, $query_name ) = @_;
428              
429             return unless exists $query{$query_name};
430              
431             if ($query_name) {
432             $sth{$query_name} //= $self->{sqlite}->prepare( $query{$query_name} );
433             return $sth{$query_name};
434             }
435              
436             return %sth if wantarray;
437              
438             return \%sth;
439             }
440              
441             sub _prepare {
442              
443             }
444              
445             =head2 get_cve
446              
447              
448             =cut
449              
450             sub get_cve {
451             my ( $self, %args ) = @_;
452              
453             $sth{get_cve_select}->execute( $args{cve_id} );
454              
455             my $row = $sth{get_cve_select}->fetchrow_hashref();
456              
457             my $frozen = $row->{cve_dump};
458              
459             my $entry = eval { thaw $frozen };
460             if (@$) {
461             carp "Storable::thaw had a major malfunction.";
462             return;
463             }
464              
465             return $entry;
466             }
467              
468             my %injection = ( OWASP => 'A1', );
469             my %xss = ( OWASP => 'A2', );
470             my %authn_session = ( OWASP => 'A3', );
471              
472             my %latest_OWASP_ten = (
473             'A1' => {
474             id => 'CWE-810',
475             members => [ 'CWE-78', 'CWE-88', 'CWE-89', 'CWE-90', 'CWE-91', ],
476             },
477             'A2' => {
478             id => 'CWE-811',
479             members => [ 'CWE-79', ],
480             },
481              
482             'A3' => {
483             id => 'CWE-812',
484             members => [ 'CWE-287', 'CWE-306', 'CWE-307', 'CWE-798', 'CWE-798', ],
485             },
486             'A4' => {
487             id => 'CWE-813',
488             members =>
489             [ 'CWE-22', 'CWE-434', 'CWE-639', 'CWE-829', 'CWE-862', 'CWE-863' ]
490             },
491             'A5' => { id => 'CWE-814', members => ['CWE-352'] },
492             'A6' => {
493             id => 'CWE-815',
494             members =>
495             [ 'CWE-209', 'CWE-219', 'CWE-250', 'CWE-538', 'CWE-552', 'CWE-732', ]
496             },
497             'A7' => {
498             id => 'CWE-816',
499             members => [ 'CWE-311', 'CWE-312', 'CWE-326', 'CWE-327', 'CWE-759', ]
500             },
501             'A8' => {
502             id => 'CWE-817',
503             members => [ 'CWE-284', 'CWE-862', 'CWE-863', ]
504             },
505             'A9' => {
506             id => 'CWE-818',
507             members => [ 'CWE-311', 'CWE-319', ]
508             },
509             'A10' => {
510             id => 'CWE-819',
511             members => [ 'CWE-601', ]
512             },
513             );
514              
515             my %owasp_idx;
516              
517             foreach my $cat (qw( A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 )) {
518             foreach my $cwe_id ( @{ $latest_OWASP_ten{$cat}->{members} } ) {
519             $owasp_idx{$cwe_id} = $cat;
520             }
521             }
522              
523             =head2 get_cwe
524              
525             my $cwe_dump = $self->get_cwe( id => $cwe_row->{id} );
526             or
527             my $cwe_dump = $self->get_cwe( cwe_id => $cwe_row->{cwe_id} );
528              
529             =cut
530              
531             sub get_cwe {
532             my ( $self, %args ) = @_;
533              
534             my $sth;
535              
536             my $arg;
537              
538             if ( exists $args{id} ) {
539             die "id [$args{id}] is malformed" unless $args{id} =~ /^\d+$/;
540             $sth = $self->{sqlite}->prepare('SELECT cwe_dump FROM cwe WHERE id=?');
541             $arg = $args{id};
542             }
543             elsif ( exists $args{cwe_id} ) {
544             die "cwe_id [$args{cwe_id}] is malformed"
545             unless $args{cwe_id} =~ /^CWE-\d+/;
546             $sth =
547             $self->{sqlite}->prepare('SELECT cwe_dump FROM cwe WHERE cwe_id=?');
548             $arg = $args{cwe_id};
549             }
550              
551             $sth->execute($arg);
552              
553             my $frozen = $sth->fetchrow_hashref()->{cwe_dump};
554              
555             my $data = eval { thaw $frozen };
556             if (@$) {
557             carp "Storable::thaw had a major malfunction.";
558             return;
559             }
560              
561             return $data;
562             }
563              
564             =head2 put_cve_idx_cpe
565              
566             my %vuln_software = ( $cpe_urn0 => [ $cve_id0,$cve_id42,... ],
567             $cpe_urn1 => [ $cve_id1,$cve_id24,... ],
568             # ...,
569             $cpe_urnN => [ $cve_id2,$cve_id3,... ],
570             );
571             $Updater->put_cve_idx_cpe( \%vuln_software );
572              
573             =cut
574              
575             my %uniq_cve_idx_cpe;
576              
577             sub put_cve_idx_cpe {
578             my ( $self, $vuln_software ) = @_;
579              
580             my @params;
581             while ( my ( $cpe_urn, $cve_id ) = ( each %$vuln_software ) ) {
582             my $cpe_pkey_id = $self->_get_cpe_id($cpe_urn);
583              
584             foreach my $id (@$cve_id) {
585             my ( $cve_pkey, $cve_friendly ) = $self->_get_cve_id($id);
586             next unless $cve_pkey;
587             next if $uniq_cve_idx_cpe{$cpe_pkey_id}->{$cve_pkey}++;
588             push( @params, [ $cpe_pkey_id, $cve_pkey ] );
589             }
590             }
591              
592             $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
593             $sth{put_cve_idx_cpe_insert}->execute(@$_) foreach (@params);
594             $self->{sqlite}->commit() or die $$self->{sqlite}->errstr;
595             return;
596             }
597              
598             =head2 put_cwe_idx_cpe
599              
600             my %vuln_software = ( $cpe_urn0 => [ $cwe_id0,$cwe_id42,... ],
601             $cpe_urn1 => [ $cwe_id1,$cwe_id24,... ],
602             # ...,
603             $cpe_urnN => [ $cwe_id2,$cwe_id3,... ],
604             );
605             $Updater->put_cwe_idx_cpe( \%weaknesses );
606              
607             =cut
608              
609             my %uniq_cwe_idx_cpe;
610              
611             sub put_cwe_idx_cpe {
612             my ( $self, $weaknesses ) = @_;
613              
614             my $initial_cwe_ids = $self->get_cwe_ids();
615              
616             my (%cpe_pkey_id) = map { $_ => $self->_get_cpe_id($_) } keys %$weaknesses;
617              
618             my @params;
619             while ( my ( $cpe_urn, $cwe_id ) = ( each %$weaknesses ) ) {
620             my $cpe_pkey_id = $cpe_pkey_id{$cpe_urn};
621              
622             foreach my $id (@$cwe_id) {
623             my $cwe_pkey_id = $initial_cwe_ids->{$id};
624              
625             unless ($cwe_pkey_id) {
626              
627             # print STDERR "no data for [$id]\n";
628             print 'x';
629             next;
630             }
631              
632             next if $uniq_cwe_idx_cpe{$cpe_pkey_id}->{$cwe_pkey_id}++;
633             push( @params, [ $cpe_pkey_id, $cwe_pkey_id ] );
634             }
635             }
636              
637             $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
638             $sth{put_cwe_idx_cpe_insert}->execute(@$_) foreach (@params);
639             $self->{sqlite}->commit();
640             return;
641             }
642              
643             =head2 update_websec_idx_cpe
644              
645             $Updater->update_websec_idx_cpe({ cpe_urn[$i] => [ $cwe_id[20], $cwe_id[7], $cwe_id[235], ... $cwe_id[$n], ],
646             cpe_urn[$j] => [ $cwe_id[42], $cwe_id[$k], $cwe_id[72], ... $cwe_id[$j], ],
647             ... => });
648              
649             =cut
650              
651             sub update_websec_idx_cpe {
652             my ($self) = @_;
653              
654             # walk through each cpe urn
655              
656             my $dbh = $self->{sqlite};
657              
658             my $q = "SELECT id, urn FROM cpe";
659             my $cpe_sth = $dbh->prepare($q);
660              
661             $q = "SELECT cve_id FROM cpe_cve_map WHERE cpe_id=?";
662             my $cve_sth = $dbh->prepare($q);
663              
664             $q = "SELECT score FROM cve WHERE id=?";
665             my $cve_score_sth = $dbh->prepare($q);
666              
667             $q = "SELECT cwe_id FROM cve_cwe_map WHERE cve_id=?";
668             my $cwe_sth = $dbh->prepare($q);
669              
670             my $cwe_idx_cpe_sth = $self->_get_sth('put_cwe_idx_cpe_insert');
671              
672             $q =
673             ( "INSERT INTO cpe_websec_score ("
674             . "cpe_urn,cat_a0,cat_a1,cat_a2,cat_a3,cat_a4,cat_a5,cat_a6,cat_a7,cat_a8,cat_a9,cat_a10"
675             . ") VALUES ("
676             . "?,?,?,?,?,?,?,?,?,?,?,?"
677             . ")" );
678             my $score_sth = $dbh->prepare($q);
679              
680             $cpe_sth->execute();
681              
682             my @idx_args = ();
683              
684             my $websec_score = {};
685              
686             while ( my $cpe_row = $cpe_sth->fetchrow_hashref() ) {
687              
688             # for each cpe, find all CVEs
689             $cve_sth->execute( $cpe_row->{id} );
690             while ( my $cve_row = $cve_sth->fetchrow_hashref() ) {
691              
692             $cve_score_sth->execute( $cve_row->{cve_id} );
693             my $cve_score_row = $cve_score_sth->fetchrow_hashref();
694              
695             my $score = $cve_score_row->{score};
696              
697             my ( $cve_pkey, $cve_friendly ) =
698             $self->_get_cve_id( $cve_row->{cve_id} );
699              
700             # for each CVE, find all CWEs
701             $cwe_sth->execute($cve_friendly);
702              
703             while ( my $cwe_row = $cwe_sth->fetchrow_hashref() ) {
704             my $cwe_id = $cwe_row->{cwe_id};
705             push( @idx_args, [ $cpe_row->{id}, $cwe_id ] );
706              
707             my $owasp_cat = $owasp_idx{$cwe_id} // 'other';
708              
709             push(
710             @{ $websec_score->{ $cpe_row->{urn} }->{$owasp_cat} },
711             {
712             cwe_id => $cwe_id,
713             cve_id => $cve_friendly,
714             score => $score,
715             },
716             );
717             }
718              
719             }
720             }
721              
722             my @websec_score;
723             foreach my $cpe_urn ( keys %$websec_score ) {
724             my $score = $websec_score->{$cpe_urn};
725             my @score;
726             foreach my $cat (qw( other A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 )) {
727             if ( exists $score->{$cat} ) {
728             my $final = 0;
729             foreach my $s ( @{ $score->{$cat} } ) {
730             $final = $s->{score} if $s->{score} > $final;
731             }
732             push( @score, $final );
733             }
734             else {
735             push( @score, 0 );
736             }
737             }
738             push( @websec_score, [ $cpe_urn, @score ] );
739             }
740              
741             $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
742             $score_sth->execute(@$_) foreach @websec_score;
743             $cwe_idx_cpe_sth->execute(@$_) foreach @idx_args;
744             $self->{sqlite}->commit();
745              
746             }
747              
748             =head2 put_cpe
749              
750              
751             =cut
752              
753             my %inserted_cpe;
754              
755             sub put_cpe {
756             my ( $self, $cpe_urn ) = @_;
757              
758             $cpe_urn = [$cpe_urn] unless ( ref $cpe_urn eq 'ARRAY' );
759              
760             my %cpe_urn = map { $_ => 1 } @$cpe_urn;
761             my $query = 'SELECT id,urn FROM cpe';
762             my $sth = $self->{sqlite}->prepare($query);
763              
764             while ( my $row = $sth->fetchrow_hashref() ) {
765             delete $cpe_urn{ $row->{cpe_urn} }
766             if exists $cpe_urn{ $row->{cpe_urn} };
767             }
768              
769             my @params;
770             foreach my $urn ( keys %cpe_urn ) {
771             next if $inserted_cpe{$urn}++;
772              
773             my (
774             $prefix, $nada, $part, $vendor, $product,
775             $version, $update, $edition, $language
776             ) = split( m{[/:]}, $urn );
777              
778             push(
779             @params,
780             [
781             $urn, $part, $vendor, $product,
782             $version, $update, $edition, $language
783             ]
784             );
785             }
786              
787             $self->{sqlite}->do('BEGIN IMMEDIATE TRANSACTION');
788             $sth{put_cpe_insert}->execute(@$_) foreach @params;
789             $self->{sqlite}->commit();
790             }
791              
792             =head2 put_cve
793              
794              
795             =cut
796              
797             sub put_cve {
798              
799             }
800              
801             =head2 put_nvd_entries
802              
803              
804             =cut
805              
806             sub put_nvd_entries {
807             my ( $self, $entries ) = @_;
808              
809             my @insert_args;
810             my @update_args;
811              
812             while ( my ( $cve_id, $orig_entry ) = ( each %$entries ) ) {
813             my $entry = {};
814              
815             foreach my $preserve ( $self->_important_fields() ) {
816             $entry->{$preserve} = $orig_entry->{$preserve}
817             if exists $orig_entry->{$preserve};
818             }
819              
820             my $frozen = nfreeze($entry);
821              
822             my $score =
823             $entry->{'vuln:cvss'}->{'cvss:base_metrics'}->{'cvss:score'};
824             my ( $pkey, $friendly ) = $self->_get_cve_id($cve_id);
825             my $sth;
826             my $cve_indexed = 0;
827              
828             # If the CVE is already in the database, update the record
829             if ($pkey) {
830             push( @update_args, [ $frozen, $score, $cve_id ] );
831             }
832             else {
833             push( @insert_args, [ $frozen, $score, $cve_id ] );
834             }
835              
836             }
837              
838             $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
839             $sth{put_cve_update}->execute(@$_) foreach @update_args;
840             $sth{put_cve_insert}->execute(@$_) foreach @insert_args;
841             $self->{sqlite}->commit();
842              
843             }
844              
845             =head2 put_cwe
846              
847             $result = $self->put_cwe( cwe_id => 'CWE-42',
848             cwe_dump => $cwe_dump );
849              
850             =cut
851              
852             my $commit_buffer = {};
853              
854             sub put_cwe {
855             my ( $self, %args ) = @_;
856              
857             my $cwe_id = $args{cwe_id};
858             my $cwe_dump = $args{cwe_dump};
859              
860             if ( exists $args{transactional} ) {
861             push( @{ $commit_buffer->{put_cwe_insert} }, [ $cwe_dump, $cwe_id ] );
862             }
863             else {
864             $sth{put_cwe_insert}->execute( $cwe_dump, $cwe_id );
865             }
866              
867             return;
868             }
869              
870             =head2 put_cwe_idx_cve
871              
872             $result = $store->put_cwe_idx_cve({ $cve_id[0] => $entry[0],
873             $cve_id[1] => $entry[1],
874             # ...
875             $cve_id[$n] => $entry[$n],
876             });
877              
878             =cut
879              
880             sub put_cwe_idx_cve {
881             my ( $self, $entries ) = @_;
882              
883             my @cwe_idx_args;
884             my $num_cwes = 0;
885             while ( my ( $cve_id, $entry ) = ( each %$entries ) ) {
886             my ( $pkey, $friendly ) = $self->_get_cve_id($cve_id);
887              
888             $num_cwes += scalar @{ $entry->{'vuln:cwe'} }
889             if exists $entry->{'vuln:cwe'};
890              
891             # index the cve->cwe relation
892             foreach my $cwe_id ( @{ $entry->{'vuln:cwe'} } ) {
893              
894             my ( $cwe_friendly, $cwe_pkey );
895              
896             if ( $cwe_id =~ /^CWE-\d+$/ ) {
897             $cwe_friendly = $cwe_id;
898             }
899             else {
900             ( $cwe_pkey, $cwe_friendly ) = $self->_get_cwe_id($cwe_id);
901             }
902              
903             next unless $cwe_friendly;
904              
905             push( @cwe_idx_args, [ $cve_id, $cwe_friendly ] );
906             }
907             }
908              
909             $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
910             $sth{put_cwe_idx_cve_insert}->execute(@$_) foreach @cwe_idx_args;
911             $self->{sqlite}->commit();
912              
913             }
914              
915             sub _commit {
916             my ( $self, $buffer_name ) = @_;
917              
918             $self->{sqlite}->do('BEGIN IMMEDIATE TRANSACTION');
919             foreach my $row ( @{ $commit_buffer->{$buffer_name} } ) {
920             my (@bound) = @$row;
921             $sth{$buffer_name}->execute(@bound);
922             }
923             $self->{sqlite}->commit();
924             delete $commit_buffer->{$buffer_name};
925             }
926              
927             =head2 get_websec_by_cpe
928              
929             my $result = $store->get_websec_by_cpe( 'cpe:/a:apache:tomcat:6.0.28' );
930             while( my $websec = shift( @{$result->{websec_results}} ) ){
931             print( "$websec->{key} - $websec->{category}: ".
932             "$websec->{score}\n" );
933             }
934              
935             =cut
936              
937             my %cat_name = (
938             cat_a0 => 'Other',
939             cat_a1 => 'Injection',
940             cat_a2 => 'Cross-Site Scripting (XSS)',
941             cat_a3 => 'Broken Authentication and Session Management',
942             cat_a4 => 'Insecure Direct Object References',
943             cat_a5 => 'Cross-Site Request Forgery (CSRF)',
944             cat_a6 => 'Security Misconfiguration',
945             cat_a7 => 'Insecure Cryptographic Storage',
946             cat_a8 => 'Failure to Restrict URL Access',
947             cat_a9 => 'Insufficient Transport Layer Protection',
948             cat_a10 => 'Unvalidated Redirects and Forwards',
949             );
950              
951             sub get_websec_by_cpe {
952             my ( $s, $self, $cpe ) = @_;
953              
954             my @websec_results;
955             my %results = ( websec_results => \@websec_results );
956              
957             my $sth = $sth{get_websec_score_select_by_cpe};
958              
959             $sth->execute($cpe);
960              
961             my $row = $sth->fetchrow_hashref();
962              
963             foreach my $key (
964             qw(cat_a0 cat_a1 cat_a2 cat_a3 cat_a4
965             cat_a5 cat_a6 cat_a7 cat_a8 cat_a9 cat_a10)
966             )
967             {
968             push(
969             @websec_results,
970             {
971             category => $cat_name{$key},
972             score => $row->{$key},
973             key => $key
974             }
975             );
976             }
977              
978             return %results if wantarray;
979              
980             return \%results;
981             }
982              
983             =head2 get_cwe_ids
984              
985             $result = $self->get_cwe_ids();
986             while( my( $cwe_id, $cwe_pkey_id ) = each %$result ){
987             ...
988             }
989              
990             =cut
991              
992             sub get_cwe_ids {
993             my ($self) = @_;
994              
995             my $result = {};
996              
997             my $sth = $self->{sqlite}->prepare("SELECT cwe_id,id FROM cwe");
998              
999             $sth->execute();
1000              
1001             while ( my $row = $sth->fetchrow_hashref() ) {
1002             $result->{ $row->{cwe_id} } = $row->{id};
1003             }
1004              
1005             return $result;
1006             }
1007              
1008             =head2 put_cwe_data
1009              
1010             $cwe_data = { View => $view_data,
1011             Category => $category_data,
1012             Weakness => $weakness_data,
1013             Compound_Element => $compound_data,
1014             };
1015              
1016             $NVD_Updater->put_cwe_data($cwe_data);
1017              
1018             =cut
1019              
1020             sub put_cwe_data {
1021             my ( $self, $weakness_data ) = @_;
1022              
1023             my @insert_entries;
1024             my @update_entries;
1025              
1026             my $insert_sth = $sth{put_cwe_insert};
1027             my $update_sth = $sth{put_cwe_update};
1028              
1029             my $initial_cwe_ids = $self->get_cwe_ids();
1030              
1031             my $count = 0;
1032             foreach my $element (qw(View Category Weakness Compound_Element)) {
1033             my $data = $weakness_data->{$element};
1034             my %cwe_pkey_id;
1035              
1036             while ( my ( $k, $entry ) = ( each %$data ) ) {
1037              
1038             my ( $cwe_pkey_id, $cwe_id ) = $self->_get_cwe_id($k);
1039              
1040             $self->{cwe_pkey_id}->{$cwe_id} = $cwe_pkey_id if $cwe_id;
1041              
1042             my $frozen = nfreeze($entry);
1043              
1044             if ($cwe_pkey_id) {
1045             push( @update_entries, [ $frozen, $cwe_pkey_id ] );
1046             }
1047             elsif ( $k =~ /^CWE-\d+$/ ) {
1048             push( @insert_entries, [ $frozen, $cwe_id ] );
1049             }
1050             else {
1051             carp "cwe id [$k] is unrecognized.\n";
1052             }
1053             print STDERR '.' if ( ++$count % 100 == 0 );
1054             }
1055             }
1056              
1057             # $self->{sqlite}->do("BEGIN IMMEDIATE TRANSACTION");
1058             # $insert_sth->execute(@$_) foreach @insert_entries;
1059             # $update_sth->execute(@$_) foreach @update_entries;
1060             # $self->{sqlite}->commit();
1061              
1062             }
1063              
1064             sub _important_fields {
1065             return qw(
1066             vuln:cve-id
1067             vuln:cvss
1068             vuln:cwe
1069             vuln:discovered-datetime
1070             vuln:published-datetime
1071             vuln:discovered-datetime
1072             vuln:last-modified-datetime
1073             vuln:security-protection
1074             vuln:vulnerable-software-list
1075             );
1076              
1077             }
1078              
1079             =head1 AUTHOR
1080              
1081             C.J. Adams-Collier, C<< <cjac at f5.com> >>
1082              
1083             =head1 BUGS
1084              
1085             Please report any bugs or feature requests to C<bug-nist-nvd-store-sqlite3 at rt.cpan.org>, or through
1086             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=NIST-NVD-Store-SQLite3>. I will be notified, and then you'll
1087             automatically be notified of progress on your bug as I make changes.
1088              
1089              
1090              
1091              
1092             =head1 SUPPORT
1093              
1094             You can find documentation for this module with the perldoc command.
1095              
1096             perldoc NIST::NVD::Store::SQLite3
1097              
1098              
1099             You can also look for information at:
1100              
1101             =over 4
1102              
1103             =item * RT: CPAN's request tracker
1104              
1105             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=NIST-NVD-Store-SQLite3>
1106              
1107             =item * AnnoCPAN: Annotated CPAN documentation
1108              
1109             L<http://annocpan.org/dist/NIST-NVD-Store-SQLite3>
1110              
1111             =item * CPAN Ratings
1112              
1113             L<http://cpanratings.perl.org/d/NIST-NVD-Store-SQLite3>
1114              
1115             =item * Search CPAN
1116              
1117             L<http://search.cpan.org/dist/NIST-NVD-Store-SQLite3/>
1118              
1119             =back
1120              
1121              
1122             =head1 ACKNOWLEDGEMENTS
1123              
1124              
1125             =head1 LICENSE AND COPYRIGHT
1126              
1127             Copyright 2012 F5 Networks, Inc.
1128              
1129             CVE(r) and CWE(tm) are marks of The MITRE Corporation and used here with
1130             permission. The information in CVE and CWE are copyright of The MITRE
1131             Corporation and also used here with permission.
1132              
1133             Please include links for CVE(r) <http://cve.mitre.org/> and CWE(tm)
1134             <http://cwe.mitre.org/> in all reproductions of these materials.
1135              
1136             This program is free software; you can redistribute it and/or modify it
1137             under the terms of either: the GNU General Public License as published
1138             by the Free Software Foundation; or the Artistic License.
1139              
1140             See http://dev.perl.org/licenses/ for more information.
1141              
1142              
1143             =cut
1144              
1145             1; # End of NIST::NVD::Store::SQLite3