2020from zipfile import ZipFile
2121
2222CURRENT_DIR = os .path .realpath (os .path .dirname ("." ))
23- EXPLOITS_DIR_ORIG = os .path .join (CURRENT_DIR , 'exploitdb-main' )
24- EXPLOITS_DIR = os .path .join (CURRENT_DIR , 'exploits' )
25- EXPLOITS_CSV = os .path .join (EXPLOITS_DIR , 'files_exploits.csv' )
26- ARCHIVE_PATH = os .path .join (CURRENT_DIR , 'master.zip' )
27- LATEST_PATH = os .path .join (CURRENT_DIR , '.latest' )
28- ARCHIVE_URL = "https://gitlab.com/exploit-database/exploitdb/-/archive/main/exploitdb-main.zip"
23+ EXPLOITS_DIR_ORIG = os .path .join (CURRENT_DIR , "exploitdb-main" )
24+ EXPLOITS_DIR = os .path .join (CURRENT_DIR , "exploits" )
25+ EXPLOITS_CSV = os .path .join (EXPLOITS_DIR , "files_exploits.csv" )
26+ ARCHIVE_PATH = os .path .join (CURRENT_DIR , "master.zip" )
27+ LATEST_PATH = os .path .join (CURRENT_DIR , ".latest" )
28+ ARCHIVE_URL = (
29+ "https://gitlab.com/exploit-database/exploitdb/-/archive/main/exploitdb-main.zip"
30+ )
2931
3032
3133class ExploitSearch (cmd .Cmd ):
32- prompt : str = ' \033 [1;31mexploitdb\033 [0m\033 [1;32m>\033 [0m '
34+ prompt : str = " \033 [1;31mexploitdb\033 [0m\033 [1;32m>\033 [0m "
3335 intro : str = (
34- '\n \033 [40m\033 [1;37m'
35- '-=[ exploitdb.py - Search exploits from exploit-db.com ]='
36- '-\033 [0m\n '
36+ "\n \033 [40m\033 [1;37m"
37+ "-=[ exploitdb.py - Search exploits from exploit-db.com ]="
38+ "-\033 [0m\n "
39+ )
40+ fields : tuple [str ] = (
41+ "id" ,
42+ "file" ,
43+ "description" ,
44+ "date" ,
45+ "author" ,
46+ "platform" ,
47+ "type" ,
48+ "port" ,
3749 )
38- fields : tuple [str ] = ('id' , 'file' , 'description' , 'date' , 'author' , 'platform' , 'type' , 'port' )
3950 search_regexes : dict [str , re .Pattern ] = {
40- ' plain' : [
51+ " plain" : [
4152 re .compile (r'(\w+)?:(?!r[\'"])([^ \'"]+)?' ),
42- re .compile (r'(\w+)?:(?:\'|")([^"\']+)?' )
53+ re .compile (r'(\w+)?:(?:\'|")([^"\']+)?' ),
4354 ],
44- 'regex' : [
45- re .compile (r'(\w+):r(?:\'|")([^"\']+)' )
46- ]
55+ "regex" : [re .compile (r'(\w+):r(?:\'|")([^"\']+)' )],
4756 }
4857 highlighted_fields_map : dict [str , list [str ]] = {
49- 'id' : ['id' ],
50- ' description' : [' description' ],
51- ' file' : [' type' , ' platform' ]
58+ "id" : ["id" ],
59+ " description" : [" description" ],
60+ " file" : [" type" , " platform" ],
5261 }
5362 csv_file : str
5463 exploits : list [dict [str , str ]]
@@ -59,9 +68,9 @@ def __init__(self, csv_file: str = EXPLOITS_CSV):
5968 self .exploits = []
6069 self .load_csv ()
6170 self .fields_value_completion = {
62- ' platform' : {e [' platform' ] for e in self .exploits },
63- ' type' : {e [' type' ] for e in self .exploits },
64- ' port' : {e [' port' ] for e in self .exploits }
71+ " platform" : {e [" platform" ] for e in self .exploits },
72+ " type" : {e [" type" ] for e in self .exploits },
73+ " port" : {e [" port" ] for e in self .exploits },
6574 }
6675 super ().__init__ ()
6776
@@ -75,35 +84,35 @@ def get_archive_etag() -> str:
7584 def download_archive ():
7685 downloaded_size = 0
7786 block_size = 4096
78- with open (ARCHIVE_PATH , 'wb' ) as outfile :
87+ with open (ARCHIVE_PATH , "wb" ) as outfile :
7988 resp = urlopen (ARCHIVE_URL )
8089 buff = resp .read (block_size )
8190 while buff :
8291 outfile .write (buff )
8392 downloaded_size += len (buff )
8493 readable_size , unit = format_bytes (downloaded_size )
85- status = f"Downloading exploits archive... { readable_size :.2f} { unit } " + " "
94+ status = (
95+ f"Downloading exploits archive... { readable_size :.2f} { unit } "
96+ + " "
97+ )
8698 sys .stdout .write (status )
87- sys .stdout .write (' \b ' * (len (status ) + 1 ))
99+ sys .stdout .write (" \b " * (len (status ) + 1 ))
88100 buff = resp .read (block_size )
89- sys .stdout .write (' \n ' )
101+ sys .stdout .write (" \n " )
90102
91103 def parse_args (self , args : str ) -> dict :
92- search_args = {
93- 'plain' : [],
94- 'regex' : []
95- }
96- if ':' not in args :
97- search_args ['plain' ].append (('description' , args ))
104+ search_args = {"plain" : [], "regex" : []}
105+ if ":" not in args :
106+ search_args ["plain" ].append (("description" , args ))
98107 else :
99108 for search_type , regexes in self .search_regexes .items ():
100109 for regex in regexes :
101110 result = regex .findall (args )
102111 if result :
103112 for field_name , pattern in result :
104113 if field_name not in self .fields :
105- pattern = f' { field_name } :{ pattern } '
106- search_args [' plain' ].append ((' description' , pattern ))
114+ pattern = f" { field_name } :{ pattern } "
115+ search_args [" plain" ].append ((" description" , pattern ))
107116 else :
108117 search_args [search_type ].append ((field_name , pattern ))
109118 return search_args
@@ -115,7 +124,7 @@ def load_csv(self, startup: bool = True):
115124 self .updatedb (etag )
116125 else :
117126 if startup :
118- print ("Checking for new database version... " , end = '' )
127+ print ("Checking for new database version... " , end = "" )
119128 with open (LATEST_PATH ) as infile :
120129 current_etag = infile .read ().strip ()
121130 latest_etag = self .get_archive_etag ()
@@ -131,12 +140,12 @@ def load_csv(self, startup: bool = True):
131140 header = entry
132141 continue
133142 exploit = dict (zip (header , entry ))
134- if exploit [' port' ] == '0' :
135- exploit [' port' ] = ' n/a'
136- if not exploit [' platform' ]:
137- exploit [' platform' ] = ' n/a'
138- if '//' in exploit [' file' ]:
139- exploit [' file' ] = exploit [' file' ].replace ('//' , '/' )
143+ if exploit [" port" ] == "0" :
144+ exploit [" port" ] = " n/a"
145+ if not exploit [" platform" ]:
146+ exploit [" platform" ] = " n/a"
147+ if "//" in exploit [" file" ]:
148+ exploit [" file" ] = exploit [" file" ].replace ("//" , "/" )
140149 self .exploits .append (exploit )
141150
142151 def search (self , search_params : str ) -> list [tuple [dict , dict ]]:
@@ -145,14 +154,14 @@ def search(self, search_params: str) -> list[tuple[dict, dict]]:
145154 for exploit in self .exploits :
146155 matching = True
147156 for search_type , search_args in args .items ():
148- if search_type == ' plain' :
157+ if search_type == " plain" :
149158 for field_name , pattern in search_args :
150159 if pattern .lower () not in exploit [field_name ].lower ():
151160 matching = False
152161 break
153162 if not matching :
154163 break
155- elif search_type == ' regex' :
164+ elif search_type == " regex" :
156165 for field_name , pattern in search_args :
157166 if re .search (pattern , exploit [field_name ], flags = re .I ) is None :
158167 matching = False
@@ -171,7 +180,7 @@ def do_search(self, line: str):
171180 results = self .search (line )
172181 for result , args in results :
173182 flattened_args = defaultdict (list )
174- for search_type in (' plain' , ' regex' ):
183+ for search_type in (" plain" , " regex" ):
175184 for k , v in args [search_type ]:
176185 flattened_args [k ].append (v )
177186 result = copy (result )
@@ -180,36 +189,41 @@ def do_search(self, line: str):
180189 if search_val in flattened_args :
181190 for pattern in flattened_args [search_val ]:
182191 re_pattern = pattern
183- if field_name not in args [' regex' ]:
192+ if field_name not in args [" regex" ]:
184193 re_pattern = re .escape (pattern )
185194 result [field_name ] = re .sub (
186195 re_pattern ,
187- lambda matchobj : '\033 [1;33m' + matchobj .group (0 ) + '\033 [0m' ,
196+ lambda matchobj : "\033 [1;33m"
197+ + matchobj .group (0 )
198+ + "\033 [0m" ,
188199 result [field_name ],
189200 flags = re .I ,
190- count = 1
201+ count = 1 ,
191202 )
192203 result_str = f"[{ result ['id' ]} ] { result ['description' ]} - { result ['file' ]} "
193204 print (result_str )
194- print ('' )
205+ print ("" )
195206
196- def complete_search (self , _text : str , line : str , _begidx : int , _endidx : int ) -> list [str ]:
207+ def complete_search (
208+ self , _text : str , line : str , _begidx : int , _endidx : int
209+ ) -> list [str ]:
197210 last_arg = line .split ()[- 1 ]
198211 if last_arg :
199- if ':' in last_arg :
200- field_name , pattern = last_arg .split (':' )
212+ if ":" in last_arg :
213+ field_name , pattern = last_arg .split (":" )
201214 if pattern :
202215 return [
203- f for f in self .fields_value_completion [field_name ]
216+ f
217+ for f in self .fields_value_completion [field_name ]
204218 if f .startswith (pattern )
205219 ]
206220 return [f for f in self .fields_value_completion [field_name ]]
207- return [f + ':' for f in self .fields if f .startswith (last_arg )]
208- return [f + ':' for f in self .fields ]
221+ return [f + ":" for f in self .fields if f .startswith (last_arg )]
222+ return [f + ":" for f in self .fields ]
209223
210224 def info (self , exploit_id : str ) -> dict :
211225 for exploit in self .exploits :
212- if exploit ['id' ] == exploit_id :
226+ if exploit ["id" ] == exploit_id :
213227 return exploit
214228 return None
215229
@@ -222,25 +236,25 @@ def do_info(self, line: str):
222236 if result is None :
223237 print (f"No exploit with this ID: { line } \n " )
224238 return
225- desc_len = len (result [' description' ])
239+ desc_len = len (result [" description" ])
226240 fstring = "{{:<13}} | {{:<{}}}" .format (desc_len )
227241 print (result )
228- print (("{{:=^{}}}" .format (desc_len + 17 )).format (' #%s ' % result ['id' ]))
229- print (fstring .format (' Filename ' , result [' file' ] + ' ' ))
230- print (fstring .format (' Description ' , result [' description' ] + ' ' ))
231- print (fstring .format (' Published ' , result [' date_published' ] + ' ' ))
232- print (fstring .format (' Updated ' , result [' date_updated' ] + ' ' ))
233- print (fstring .format (' Author ' , result [' author' ] + ' ' ))
234- print (fstring .format (' Platform ' , result [' platform' ] + ' ' ))
235- print (fstring .format (' Type ' , result [' type' ] + ' ' ))
236- print (fstring .format (' Port ' , result [' port' ] + ' ' ))
237- print ((desc_len + 17 ) * '=' + ' \n ' )
242+ print (("{{:=^{}}}" .format (desc_len + 17 )).format (" #%s " % result ["id" ]))
243+ print (fstring .format (" Filename " , result [" file" ] + " " ))
244+ print (fstring .format (" Description " , result [" description" ] + " " ))
245+ print (fstring .format (" Published " , result [" date_published" ] + " " ))
246+ print (fstring .format (" Updated " , result [" date_updated" ] + " " ))
247+ print (fstring .format (" Author " , result [" author" ] + " " ))
248+ print (fstring .format (" Platform " , result [" platform" ] + " " ))
249+ print (fstring .format (" Type " , result [" type" ] + " " ))
250+ print (fstring .format (" Port " , result [" port" ] + " " ))
251+ print ((desc_len + 17 ) * "=" + " \n " )
238252
239253 def complete_info (self , text : str , _line : str , _begidx : int , _endidx : int ):
240254 if not text :
241- return [e ['id' ] for e in self .exploits ]
255+ return [e ["id" ] for e in self .exploits ]
242256 else :
243- return [e ['id' ] for e in self .exploits if e ['id' ].startswith (text )]
257+ return [e ["id" ] for e in self .exploits if e ["id" ].startswith (text )]
244258
245259 def updatedb (self , etag : str ):
246260 self .download_archive ()
@@ -254,7 +268,7 @@ def updatedb(self, etag: str):
254268 os .rename (EXPLOITS_DIR_ORIG , EXPLOITS_DIR )
255269 os .chmod (EXPLOITS_CSV , 0o644 )
256270 self .exploits = []
257- with open (LATEST_PATH , 'w' ) as outfile :
271+ with open (LATEST_PATH , "w" ) as outfile :
258272 outfile .write (etag )
259273 self .load_csv (startup = False )
260274 print ("OK\n " )
@@ -273,9 +287,9 @@ def do_show(self, line: str):
273287 Usage: show exploit_id|exploit_path
274288 """
275289 if line .isdigit ():
276- field = 'id'
290+ field = "id"
277291 else :
278- field = ' file'
292+ field = " file"
279293 found = False
280294 for exploit in self .exploits :
281295 if exploit [field ] == line :
@@ -284,45 +298,51 @@ def do_show(self, line: str):
284298 if not found :
285299 print (f"Exploit not found: { line } \n " )
286300 else :
287- sploit_path = os .path .join (EXPLOITS_DIR , exploit [' file' ])
301+ sploit_path = os .path .join (EXPLOITS_DIR , exploit [" file" ])
288302 with open (sploit_path ) as infile :
289303 print (infile .read ())
290- print ('' )
304+ print ("" )
291305
292- def complete_show (self , text : str , _line : str , _begidx : str , _endidx : str ) -> list [str ]:
306+ def complete_show (
307+ self , text : str , _line : str , _begidx : str , _endidx : str
308+ ) -> list [str ]:
293309 completions = []
294310 if not text :
295- completions .extend ([s ['id' ] for s in self .exploits ])
296- completions .extend ([s [' file' ] for s in self .exploits ])
311+ completions .extend ([s ["id" ] for s in self .exploits ])
312+ completions .extend ([s [" file" ] for s in self .exploits ])
297313 else :
298- completions .extend ([s ['id' ] for s in self .exploits if s ['id' ].startswith (text )])
299- completions .extend ([s ['file' ] for s in self .exploits if s ['file' ].startswith (text )])
314+ completions .extend (
315+ [s ["id" ] for s in self .exploits if s ["id" ].startswith (text )]
316+ )
317+ completions .extend (
318+ [s ["file" ] for s in self .exploits if s ["file" ].startswith (text )]
319+ )
300320 return completions
301321
302322 @staticmethod
303323 def do_EOF (self ):
304324 return True
305325
306326
307- def format_bytes (size : int | float ) -> tuple [float , str ]:
327+ def format_bytes (size : int | float ) -> tuple [float , str ]:
308328 multiple_units = ["KB" , "MB" , "GB" ]
309329 unit = "B"
310330 for u in multiple_units :
311- if size >= 1024. :
312- size /= 1024.
331+ if size >= 1024.0 :
332+ size /= 1024.0
313333 unit = u
314334 return size , unit
315335
316336
317337def main (restarted : bool = False ):
318338 es = ExploitSearch ()
319339 if restarted :
320- es .intro = ' \n '
340+ es .intro = " \n "
321341 try :
322342 es .cmdloop ()
323343 except KeyboardInterrupt :
324344 main (True )
325345
326346
327- if __name__ == ' __main__' :
347+ if __name__ == " __main__" :
328348 main ()
0 commit comments