Skip to content

Commit 1d74adc

Browse files
committed
Add script to export findings to CSV (V2)
1 parent 3adeffe commit 1d74adc

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

findings_to_csv_v2.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env python
2+
"""
3+
Export all findings to CSV format
4+
5+
Run:
6+
7+
$ python3 findings_to_csv_v2.py --severity <OPTIONAL_SEVERITY (LOW|MEDIUM|HIGH|CRITICAL)> --state <OPTIONAL_STATE (FIXED|NOTFIXED|ACCEPTED|RETESTING|INVALID)> --search <OPTIONAL_SEARCH_STRING> --output <OUTPUT_FILE_PATH>
8+
9+
Multiple severities and states can be specified by repeating the argument:
10+
$ python3 findings_to_csv_v2.py --severity HIGH --severity CRITICAL --state NOTFIXED --state ACCEPTED -o output.csv
11+
12+
"""
13+
import argparse
14+
import requests
15+
import csv
16+
from urllib.parse import urljoin, quote
17+
from datetime import datetime
18+
19+
# Define the JWT or it will be asked when you run the script
20+
jwt_token = None
21+
22+
length = 50
23+
24+
api_base_url = 'https://api.probely.com'
25+
findings_endpoint = urljoin(api_base_url, "findings/")
26+
27+
SEVERITY_MAP = {
28+
10: 'LOW',
29+
20: 'MEDIUM',
30+
30: 'HIGH',
31+
40: 'CRITICAL'
32+
}
33+
SEVERITY_MAP_REVERSE = {v: k for k, v in SEVERITY_MAP.items()}
34+
35+
def main():
36+
parser = argparse.ArgumentParser()
37+
parser.add_argument('--severity', help='Severity (supports multiple items)', required=False, action='append', choices=[
38+
'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'])
39+
parser.add_argument('--state', help='State (supports multiple items)', required=False, action='append', choices=[
40+
'FIXED', 'NOTFIXED', 'ACCEPTED', 'RETESTING', 'INVALID'])
41+
parser.add_argument('--search', help='Search string', required=False)
42+
parser.add_argument('-o', '--output', help='Output CSV file', type=argparse.FileType('w'), required=True)
43+
args = parser.parse_args()
44+
45+
if jwt_token is None:
46+
token = input("API Token:")
47+
else:
48+
token = jwt_token
49+
50+
if token is None or token == '':
51+
print('Error: JWT is required')
52+
return
53+
headers = {'Authorization': "JWT {}".format(token)}
54+
55+
params = {
56+
'length': length,
57+
'ordering': '-last_found',
58+
'exclude': ['requests', 'evidence', 'scans', 'fix'],
59+
}
60+
if args.severity is not None:
61+
params['severity'] = [SEVERITY_MAP_REVERSE[s.upper()] for s in args.severity]
62+
if args.state is not None:
63+
params['state'] = [s.lower() for s in args.state]
64+
if args.search is not None:
65+
params['search'] = args.search
66+
67+
csv_writer = csv.writer(
68+
args.output, delimiter=",", quotechar='"', quoting=csv.QUOTE_ALL
69+
)
70+
row = [
71+
'ID',
72+
'Target ID',
73+
'Target Name',
74+
'Target URL',
75+
'Finding',
76+
'Method',
77+
'Endpoint/Path',
78+
'Parameter',
79+
'Severity',
80+
'State',
81+
'Last Found',
82+
'Snyk URL'
83+
]
84+
csv_writer.writerow(row)
85+
86+
current_page = 1
87+
total_pages = 1
88+
total_count = 0
89+
90+
while current_page <= total_pages:
91+
params['page'] = current_page
92+
response_findings = requests.get(
93+
findings_endpoint,
94+
headers=headers,
95+
params=params
96+
)
97+
# print(f"DEBUG: Request URL: {response_findings.url}")
98+
response_findings.raise_for_status()
99+
response_json = response_findings.json()
100+
101+
total_pages = response_json['page_total']
102+
total_count = response_json['count']
103+
findings_res = response_json['results']
104+
105+
for finding in findings_res:
106+
row = [
107+
finding['id'],
108+
finding['target']['id'],
109+
finding['target']['site']['name'],
110+
finding['target']['site']['url'],
111+
finding['definition']['name'],
112+
finding['method'].upper() if finding.get('method') else '-',
113+
finding['path'],
114+
finding['parameter'] if finding.get('parameter') else 'NA',
115+
SEVERITY_MAP.get(finding['severity'], 'UNKNOWN'),
116+
finding['state'].upper(),
117+
datetime.strptime(finding['last_found'], '%Y-%m-%dT%H:%M:%S.%fZ').strftime('%Y-%m-%d %H:%M:%S'),
118+
f'https://plus.probely.app/targets/{finding["target"]["id"]}/findings/{finding["id"]}'
119+
]
120+
csv_writer.writerow(row)
121+
# print(row)
122+
123+
current_page += 1
124+
125+
print('Done')
126+
127+
if __name__ == '__main__':
128+
main()

0 commit comments

Comments
 (0)