5
5
import logging
6
6
import os
7
7
import shutil
8
+ import types
8
9
9
10
import orjson
10
11
from rich .console import Console
11
- from rich .syntax import Syntax
12
+ from rich .live import Live
13
+ from rich .markdown import Markdown
12
14
from rich .table import Table
13
15
14
16
from vdb .lib import config , db6 as db_lib , search
15
17
from vdb .lib .aqua import AquaSource
18
+ from vdb .lib .cve_model import CVE
16
19
from vdb .lib .gha import GitHubSource
17
20
from vdb .lib .osv import OSVSource
18
21
@@ -85,27 +88,62 @@ def build_args():
85
88
parser .add_argument (
86
89
"--search" ,
87
90
dest = "search" ,
88
- help = "Search for the package or CVE ID in the database. Use purl, cpe, or colon-separated values." ,
91
+ help = "Search for the package or CVE ID in the database. Use purl, cpe, or git http url." ,
92
+ )
93
+ parser .add_argument (
94
+ "--bom" ,
95
+ dest = "bom_file" ,
96
+ help = "Search for packages in the CycloneDX BOM file." ,
89
97
)
90
98
return parser .parse_args ()
91
99
92
100
101
+ def add_table_row (table : Table , res : dict , added_row_keys : dict ):
102
+ # matched_by is the purl or cpe string
103
+ row_key = f"""{ res ["matched_by" ]} |res.get("source_data_hash")"""
104
+ # Filter duplicate rows from getting printed
105
+ if added_row_keys .get (row_key ):
106
+ return
107
+ source_data : CVE = res .get ("source_data" )
108
+ description = ""
109
+ if (
110
+ source_data .root .containers .cna
111
+ and source_data .root .containers .cna .descriptions
112
+ and source_data .root .containers .cna .descriptions .root
113
+ ):
114
+ description = (
115
+ source_data .root .containers .cna .descriptions .root [0 ]
116
+ .value .replace ("\\ n" , "\n " )
117
+ .replace ("\\ t" , " " )
118
+ )
119
+ table .add_row (
120
+ res .get ("cve_id" ),
121
+ res .get ("matched_by" ),
122
+ Markdown (description , justify = "left" , hyperlinks = True ),
123
+ )
124
+ added_row_keys [row_key ] = True
125
+
126
+
93
127
def print_results (results ):
94
- table = Table (title = "VDB Results" )
128
+ added_row_keys = {}
129
+ table = Table (title = "VDB Results" , show_lines = True )
95
130
table .add_column ("CVE" , justify = "left" )
96
- table .add_column ("Type" )
97
- table .add_column ("Namespace" )
98
- table .add_column ("Name" )
99
- table .add_column ("Hash" )
100
- table .add_column ("Source Data" )
101
- for res in results :
102
- table .add_row (res .get ("cve_id" ), res .get ("type" ),
103
- res .get ("namespace" , "" ), res .get ("name" ),
104
- res .get ("source_data_hash" ),
105
- Syntax (orjson .dumps (
106
- res .get ("source_data" ).model_dump (mode = "json" , exclude_none = True ),
107
- option = orjson .OPT_INDENT_2 | orjson .OPT_APPEND_NEWLINE ).decode ("utf-8" , errors = "ignore" ), "json" , word_wrap = True ))
108
- console .print (table )
131
+ table .add_column ("Locator" )
132
+ table .add_column ("Description" )
133
+ if isinstance (results , types .GeneratorType ):
134
+ with Live (
135
+ table , console = console , refresh_per_second = 4 , vertical_overflow = "visible"
136
+ ):
137
+ for result_gen in results :
138
+ if isinstance (result_gen , dict ):
139
+ add_table_row (table , result_gen , added_row_keys )
140
+ if isinstance (result_gen , types .GeneratorType ):
141
+ for res in result_gen :
142
+ add_table_row (table , res , added_row_keys )
143
+ elif isinstance (results , list ):
144
+ for res in results :
145
+ add_table_row (table , res )
146
+ console .print (table )
109
147
110
148
111
149
def main ():
@@ -133,12 +171,18 @@ def main():
133
171
LOG .info ("Refreshing %s" , s .__class__ .__name__ )
134
172
s .refresh ()
135
173
cve_data_count , cve_index_count = db_lib .stats ()
136
- console .print ("cve_data_count" , cve_data_count , "cve_index_count" , cve_index_count )
174
+ console .print (
175
+ "cve_data_count" , cve_data_count , "cve_index_count" , cve_index_count
176
+ )
137
177
db_lib .optimize_and_close_all ()
138
178
if args .search :
139
179
if args .search .startswith ("pkg:" ):
140
180
results = search .search_by_purl_like (args .search , with_data = True )
141
- elif args .search .startswith ("CVE-" ) or args .search .startswith ("GHSA-" ) or args .search .startswith ("MAL-" ):
181
+ elif (
182
+ args .search .startswith ("CVE-" )
183
+ or args .search .startswith ("GHSA-" )
184
+ or args .search .startswith ("MAL-" )
185
+ ):
142
186
results = search .search_by_cve (args .search , with_data = True )
143
187
elif args .search .startswith ("http" ):
144
188
results = search .search_by_url (args .search , with_data = True )
@@ -148,6 +192,10 @@ def main():
148
192
print_results (results )
149
193
else :
150
194
console .print ("No results found!" )
195
+ elif args .bom_file :
196
+ if os .path .exists (args .bom_file ):
197
+ results_generator = search .search_by_cdx_bom (args .bom_file , with_data = True )
198
+ print_results (results_generator )
151
199
152
200
153
201
if __name__ == "__main__" :
0 commit comments