Skip to content

Commit c3468c5

Browse files
committed
black
1 parent e81cdc4 commit c3468c5

File tree

1 file changed

+103
-83
lines changed

1 file changed

+103
-83
lines changed

exploitdb.py

Lines changed: 103 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,44 @@
2020
from zipfile import ZipFile
2121

2222
CURRENT_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

3133
class 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

317337
def 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

Comments
 (0)