Skip to content

Commit 661f264

Browse files
committed
Delete VulnerabilityLog and print_report, make formatters/text.py and json.py, -j for JSON option, refactor CALL_IDENTIFIER to tilde because of /uUGLYNUMBERS json output, refactor super calls of the subclasses of Vulnerability to be silky smooth with kwargs, implement as_dict methods for Node and vulnerability types, make tests pass with these changes, fixed a few flake8y things
1 parent 184cdd3 commit 661f264

26 files changed

+526
-509
lines changed

func_counter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ def visit_Call(self, node):
2020

2121
def visit_FunctionDef(self, node):
2222
if node.name in functions:
23-
node.name += '¤'
23+
node.name += '~'
2424
functions[node.name] = len(node.body)
2525
for n in node.body:
2626
self.visit(n)
2727

2828
def visit_ClassDef(self, node):
2929
if node.name in classes:
30-
node.name += '¤'
30+
node.name += '~'
3131
classes[node.name] = len(node.body)
3232
for n in node.body:
3333
self.visit(n)

pyt/__main__.py

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
from .draw import draw_cfgs, draw_lattices
2222
from .expr_visitor import make_cfg
2323
from .fixed_point import analyse
24+
from .formatters import (
25+
json,
26+
text
27+
)
2428
from .framework_adaptor import FrameworkAdaptor
2529
from .framework_helper import (
2630
is_django_view_function,
@@ -93,7 +97,7 @@ def parse_args(args):
9397
help='Input trigger word file.',
9498
type=str,
9599
default=default_trigger_word_file)
96-
parser.add_argument('-b', '--blackbox-mapping-file',
100+
parser.add_argument('-m', '--blackbox-mapping-file',
97101
help='Input blackbox mapping file.',
98102
type=str,
99103
default=default_blackbox_mapping_file)
@@ -111,6 +115,10 @@ def parse_args(args):
111115
' create a database.', action='store_true')
112116
parser.add_argument('-dl', '--draw-lattice',
113117
nargs='+', help='Draws a lattice.')
118+
parser.add_argument('-j', '--json',
119+
help='Prints JSON instead of report.',
120+
action='store_true',
121+
default=False)
114122

115123
analysis_group = parser.add_mutually_exclusive_group()
116124
analysis_group.add_argument('-li', '--liveness',
@@ -177,7 +185,7 @@ def parse_args(args):
177185
return parser.parse_args(args)
178186

179187

180-
def analyse_repo(github_repo, analysis_type, ui_mode):
188+
def analyse_repo(args, github_repo, analysis_type, ui_mode):
181189
cfg_list = list()
182190
directory = os.path.dirname(github_repo.path)
183191
project_modules = get_modules(directory)
@@ -193,7 +201,7 @@ def analyse_repo(github_repo, analysis_type, ui_mode):
193201

194202
initialize_constraint_table(cfg_list)
195203
analyse(cfg_list, analysis_type=analysis_type)
196-
vulnerability_log = find_vulnerabilities(
204+
vulnerabilities = find_vulnerabilities(
197205
cfg_list,
198206
analysis_type,
199207
ui_mode,
@@ -202,7 +210,7 @@ def analyse_repo(github_repo, analysis_type, ui_mode):
202210
args.trigger_word_file
203211
)
204212
)
205-
return vulnerability_log
213+
return vulnerabilities
206214

207215

208216
def main(command_line_args=sys.argv[1:]):
@@ -225,9 +233,12 @@ def main(command_line_args=sys.argv[1:]):
225233
repos = get_repos(args.git_repos)
226234
for repo in repos:
227235
repo.clone()
228-
vulnerability_log = analyse_repo(repo, analysis, ui_mode)
229-
vulnerability_log.print_report()
230-
if not vulnerability_log.vulnerabilities:
236+
vulnerabilities = analyse_repo(args, repo, analysis, ui_mode)
237+
if args.json:
238+
json.report(vulnerabilities, sys.stdout)
239+
else:
240+
text.report(vulnerabilities, sys.stdout)
241+
if not vulnerabilities:
231242
repo.clean_up()
232243
exit()
233244

@@ -239,7 +250,8 @@ def main(command_line_args=sys.argv[1:]):
239250
analysis,
240251
analyse_repo,
241252
args.csv_path,
242-
ui_mode
253+
ui_mode,
254+
args
243255
)
244256
exit()
245257

@@ -278,7 +290,7 @@ def main(command_line_args=sys.argv[1:]):
278290

279291
analyse(cfg_list, analysis_type=analysis)
280292

281-
vulnerability_log = find_vulnerabilities(
293+
vulnerabilities = find_vulnerabilities(
282294
cfg_list,
283295
analysis,
284296
ui_mode,
@@ -287,17 +299,20 @@ def main(command_line_args=sys.argv[1:]):
287299
args.trigger_word_file
288300
)
289301
)
290-
vulnerability_log.print_report()
302+
if args.json:
303+
json.report(vulnerabilities, sys.stdout)
304+
else:
305+
text.report(vulnerabilities, sys.stdout)
291306

292307
if args.draw_cfg:
293308
if args.output_filename:
294309
draw_cfgs(cfg_list, args.output_filename)
295310
else:
296311
draw_cfgs(cfg_list)
297312
if args.print:
298-
l = print_lattice(cfg_list, analysis)
313+
lattice = print_lattice(cfg_list, analysis)
299314

300-
print_table(l)
315+
print_table(lattice)
301316
for i, e in enumerate(cfg_list):
302317
print('############## CFG number: ', i)
303318
print(e)
@@ -311,7 +326,7 @@ def main(command_line_args=sys.argv[1:]):
311326
pprint(project_modules)
312327

313328
if args.create_database:
314-
create_database(cfg_list, vulnerability_log)
329+
create_database(cfg_list, vulnerabilities)
315330
if args.draw_lattice:
316331
draw_lattices(cfg_list)
317332

@@ -325,7 +340,7 @@ def main(command_line_args=sys.argv[1:]):
325340
cfg_to_file(cfg_list)
326341
verbose_cfg_to_file(cfg_list)
327342
lattice_to_file(cfg_list, analysis)
328-
vulnerabilities_to_file(vulnerability_log)
343+
vulnerabilities_to_file(vulnerabilities)
329344
else:
330345
if args.def_use_chain:
331346
def_use_chain_to_file(cfg_list)
@@ -338,7 +353,7 @@ def main(command_line_args=sys.argv[1:]):
338353
if args.lattice:
339354
lattice_to_file(cfg_list, analysis)
340355
if args.vulnerabilities:
341-
vulnerabilities_to_file(vulnerability_log)
356+
vulnerabilities_to_file(vulnerabilities)
342357

343358

344359
if __name__ == '__main__':

pyt/analysis_base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""This module contains a base class for the analysis component used in PyT."""
2-
from abc import ABCMeta, abstractmethod
2+
3+
from abc import (
4+
ABCMeta,
5+
abstractmethod
6+
)
37

48

59
class AnalysisBase(metaclass=ABCMeta):

pyt/ast_helper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""This module contains helper function.
22
Useful when working with the ast module."""
3+
34
import ast
45
import os
56
import subprocess

pyt/draw.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Draws CFG."""
2+
23
import argparse
34
from itertools import permutations
45
from subprocess import call
@@ -103,7 +104,6 @@ def draw_cfg(cfg, output_filename='output'):
103104

104105

105106
class Node():
106-
107107
def __init__(self, s, parent, children=None):
108108
self.s = s
109109
self.parent = parent

pyt/expr_visitor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ def return_handler(
451451
for node in function_nodes:
452452
# Only `Return`s and `Raise`s can be of type ConnectToExitNode
453453
if isinstance(node, ConnectToExitNode):
454-
# Create e.g. ¤call_1 = ret_func_foo RestoreNode
454+
# Create e.g. ~call_1 = ret_func_foo RestoreNode
455455
LHS = CALL_IDENTIFIER + 'call_' + str(saved_function_call_index)
456456
RHS = 'ret_' + get_call_names_as_string(call_node.func)
457457
return_node = RestoreNode(
@@ -478,11 +478,11 @@ def process_function(self, call_node, definition):
478478
Visit and get function nodes. (visit_and_get_function_nodes)
479479
Loop through each save_N_LHS node and create an e.g.
480480
foo = save_1_foo or, if foo was a call arg, foo = arg_mapping[foo]. (restore_saved_local_scope)
481-
Create e.g. ¤call_1 = ret_func_foo RestoreNode. (return_handler)
481+
Create e.g. ~call_1 = ret_func_foo RestoreNode. (return_handler)
482482
483483
Notes:
484484
Page 31 in the original thesis, but changed a little.
485-
We don't have to return the ¤call_1 = ret_func_foo RestoreNode made in return_handler,
485+
We don't have to return the ~call_1 = ret_func_foo RestoreNode made in return_handler,
486486
because it's the last node anyway, that we return in this function.
487487
e.g. ret_func_foo gets assigned to visit_Return.
488488

pyt/formatters/json.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""This formatter outputs the issues in JSON."""
2+
3+
import json
4+
from datetime import datetime
5+
6+
7+
def report(
8+
vulnerabilities,
9+
fileobj
10+
):
11+
"""
12+
Prints issues in JSON format.
13+
14+
Args:
15+
vulnerabilities: list of vulnerabilities to report
16+
fileobj: The output file object, which may be sys.stdout
17+
"""
18+
TZ_AGNOSTIC_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
19+
time_string = datetime.utcnow().strftime(TZ_AGNOSTIC_FORMAT)
20+
21+
machine_output = {
22+
'generated_at': time_string,
23+
'vulnerabilities': [vuln.as_dict() for vuln in vulnerabilities]
24+
}
25+
26+
result = json.dumps(
27+
machine_output,
28+
indent=4,
29+
sort_keys=True
30+
)
31+
32+
with fileobj:
33+
fileobj.write(result)

pyt/formatters/text.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""This formatter outputs the issues as plain text."""
2+
3+
4+
def report(
5+
vulnerabilities,
6+
fileobj
7+
):
8+
"""
9+
Prints issues in text format.
10+
11+
Args:
12+
vulnerabilities: list of vulnerabilities to report
13+
fileobj: The output file object, which may be sys.stdout
14+
"""
15+
number_of_vulnerabilities = len(vulnerabilities)
16+
with fileobj:
17+
if number_of_vulnerabilities == 1:
18+
fileobj.write('%s vulnerability found:' % number_of_vulnerabilities)
19+
else:
20+
fileobj.write('%s vulnerabilities found:' % number_of_vulnerabilities)
21+
22+
for i, vulnerability in enumerate(vulnerabilities, start=1):
23+
fileobj.write('Vulnerability {}:\n{}\n'.format(i, vulnerability))

pyt/framework_adaptor.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
"""A generic framework adaptor that leaves route criteria to the caller."""
2+
23
import ast
34

45
from .ast_helper import Arguments
56
from .expr_visitor import make_cfg
67
from .module_definitions import project_definitions
7-
from .node_types import (
8-
AssignmentNode,
9-
TaintedNode
10-
)
8+
from .node_types import TaintedNode
119

1210

1311
class FrameworkAdaptor():

pyt/github_search.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def get_dates(start_date, end_date=date.today(), interval=7):
197197
delta.days % interval))
198198

199199

200-
def scan_github(search_string, start_date, analysis_type, analyse_repo_func, csv_path, ui_mode):
200+
def scan_github(search_string, start_date, analysis_type, analyse_repo_func, csv_path, ui_mode, other_args):
201201
analyse_repo = analyse_repo_func
202202
for d in get_dates(start_date, interval=7):
203203
q = Query(SEARCH_REPO_URL, search_string,
@@ -214,27 +214,27 @@ def scan_github(search_string, start_date, analysis_type, analyse_repo_func, csv
214214
try:
215215
r.clone()
216216
except NoEntryPathError as err:
217-
save_repo_scan(repo, r.path, vulnerability_log=None, error=err)
217+
save_repo_scan(repo, r.path, vulnerabilities=None, error=err)
218218
continue
219219
except:
220-
save_repo_scan(repo, r.path, vulnerability_log=None, error='Other Error Unknown while cloning :-(')
220+
save_repo_scan(repo, r.path, vulnerabilities=None, error='Other Error Unknown while cloning :-(')
221221
continue
222222
try:
223-
vulnerability_log = analyse_repo(r, analysis_type, ui_mode)
224-
if vulnerability_log.vulnerabilities:
225-
save_repo_scan(repo, r.path, vulnerability_log)
223+
vulnerabilities = analyse_repo(other_args, r, analysis_type, ui_mode)
224+
if vulnerabilities:
225+
save_repo_scan(repo, r.path, vulnerabilities)
226226
add_repo_to_csv(csv_path, r)
227227
else:
228-
save_repo_scan(repo, r.path, vulnerability_log=None)
228+
save_repo_scan(repo, r.path, vulnerabilities=None)
229229
r.clean_up()
230230
except SyntaxError as err:
231-
save_repo_scan(repo, r.path, vulnerability_log=None, error=err)
231+
save_repo_scan(repo, r.path, vulnerabilities=None, error=err)
232232
except IOError as err:
233-
save_repo_scan(repo, r.path, vulnerability_log=None, error=err)
233+
save_repo_scan(repo, r.path, vulnerabilities=None, error=err)
234234
except AttributeError as err:
235-
save_repo_scan(repo, r.path, vulnerability_log=None, error=err)
235+
save_repo_scan(repo, r.path, vulnerabilities=None, error=err)
236236
except:
237-
save_repo_scan(repo, r.path, vulnerability_log=None, error='Other Error Unknown :-(')
237+
save_repo_scan(repo, r.path, vulnerabilities=None, error='Other Error Unknown :-(')
238238

239239
if __name__ == '__main__':
240240
for x in get_dates(date(2010, 1, 1), interval=93):

0 commit comments

Comments
 (0)