Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 853300b

Browse files
Merge pull request #129 from omergunal/patch-4
-r Recursive option
2 parents 01397dd + 2afc177 commit 853300b

File tree

4 files changed

+109
-68
lines changed

4 files changed

+109
-68
lines changed

‎pyt/__main__.py‎

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,25 @@
3030
)
3131

3232

33-
def main(command_line_args=sys.argv[1:]): # noqa: C901
33+
def discover_files(targets, excluded_files, recursive=False):
34+
included_files = list()
35+
excluded_list = excluded_files.split(",")
36+
for target in targets:
37+
if os.path.isdir(target):
38+
for root, dirs, files in os.walk(target):
39+
for f in files:
40+
fullpath = os.path.join(root, f)
41+
if os.path.splitext(fullpath)[1] == '.py' and fullpath.split("/")[-1] not in excluded_list:
42+
included_files.append(fullpath)
43+
if not recursive:
44+
break
45+
else:
46+
if target not in excluded_list:
47+
included_files.append(target)
48+
return included_files
49+
50+
51+
def main(command_line_args=sys.argv[1:]):
3452
args = parse_args(command_line_args)
3553

3654
ui_mode = UImode.NORMAL
@@ -39,60 +57,67 @@ def main(command_line_args=sys.argv[1:]): # noqa: C901
3957
elif args.trim_reassigned_in:
4058
ui_mode = UImode.TRIM
4159

42-
path = os.path.normpath(args.filepath)
60+
files = discover_files(
61+
args.targets,
62+
args.excluded_paths,
63+
args.recursive
64+
)
65+
66+
for path in files:
67+
vulnerabilities = list()
68+
if args.ignore_nosec:
69+
nosec_lines = set()
70+
else:
71+
file = open(path, 'r')
72+
lines = file.readlines()
73+
nosec_lines = set(
74+
lineno for
75+
(lineno, line) in enumerate(lines, start=1)
76+
if '#nosec' in line or '# nosec' in line
77+
)
4378

44-
if args.ignore_nosec:
45-
nosec_lines = set()
46-
else:
47-
file = open(path, 'r')
48-
lines = file.readlines()
49-
nosec_lines = set(
50-
lineno for
51-
(lineno, line) in enumerate(lines, start=1)
52-
if '#nosec' in line or '# nosec' in line
53-
)
79+
if args.project_root:
80+
directory = os.path.normpath(args.project_root)
81+
else:
82+
directory = os.path.dirname(path)
83+
project_modules = get_modules(directory)
84+
local_modules = get_directory_modules(directory)
85+
tree = generate_ast(path)
5486

55-
if args.project_root:
56-
directory = os.path.normpath(args.project_root)
57-
else:
58-
directory = os.path.dirname(path)
59-
project_modules = get_modules(directory)
60-
local_modules = get_directory_modules(directory)
87+
cfg = make_cfg(
88+
tree,
89+
project_modules,
90+
local_modules,
91+
path
92+
)
93+
cfg_list = [cfg]
6194

62-
tree = generate_ast(path)
6395

64-
cfg = make_cfg(
65-
tree,
66-
project_modules,
67-
local_modules,
68-
path
69-
)
70-
cfg_list = [cfg]
71-
framework_route_criteria = is_flask_route_function
72-
if args.adaptor:
73-
if args.adaptor.lower().startswith('e'):
74-
framework_route_criteria = is_function
75-
elif args.adaptor.lower().startswith('p'):
76-
framework_route_criteria = is_function_without_leading_
77-
elif args.adaptor.lower().startswith('d'):
78-
framework_route_criteria = is_django_view_function
79-
# Add all the route functions to the cfg_list
80-
FrameworkAdaptor(
81-
cfg_list,
82-
project_modules,
83-
local_modules,
84-
framework_route_criteria
85-
)
96+
framework_route_criteria = is_flask_route_function
97+
if args.adaptor:
98+
if args.adaptor.lower().startswith('e'):
99+
framework_route_criteria = is_function
100+
elif args.adaptor.lower().startswith('p'):
101+
framework_route_criteria = is_function_without_leading_
102+
elif args.adaptor.lower().startswith('d'):
103+
framework_route_criteria = is_django_view_function
104+
# Add all the route functions to the cfg_list
105+
FrameworkAdaptor(
106+
cfg_list,
107+
project_modules,
108+
local_modules,
109+
framework_route_criteria
110+
)
86111

87-
initialize_constraint_table(cfg_list)
88-
analyse(cfg_list)
89-
vulnerabilities=find_vulnerabilities(
90-
cfg_list,
91-
ui_mode,
92-
args.blackbox_mapping_file,
93-
args.trigger_word_file,
94-
nosec_lines
95-
)
112+
initialize_constraint_table(cfg_list)
113+
analyse(cfg_list)
114+
vulnerabilities.extend(find_vulnerabilities(
115+
cfg_list,
116+
ui_mode,
117+
args.blackbox_mapping_file,
118+
args.trigger_word_file,
119+
nosec_lines
120+
))
96121

97122
if args.baseline:
98123
vulnerabilities = get_vulnerabilities_not_in_baseline(

‎pyt/usage.py‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ def valid_date(s):
3030
def _add_required_group(parser):
3131
required_group = parser.add_argument_group('required arguments')
3232
required_group.add_argument(
33-
'-f', '--filepath',
34-
help='Path to the file that should be analysed.',
35-
type=str
33+
'targets', metavar='targets', type=str, nargs='+',
34+
help='source file(s) or directory(s) to be tested'
3635
)
3736

3837

@@ -91,6 +90,17 @@ def _add_optional_group(parser):
9190
action='store_true',
9291
help='do not skip lines with # nosec comments'
9392
)
93+
optional_group.add_argument(
94+
'-r', '--recursive', dest='recursive',
95+
action='store_true', help='find and process files in subdirectories'
96+
)
97+
optional_group.add_argument(
98+
'-x', '--exclude',
99+
dest='excluded_paths',
100+
action='store',
101+
default='',
102+
help='Separate files with commas'
103+
)
94104

95105

96106
def _add_print_group(parser):
@@ -110,8 +120,8 @@ def _add_print_group(parser):
110120

111121

112122
def _check_required_and_mutually_exclusive_args(parser, args):
113-
if args.filepath is None:
114-
parser.error('The -f/--filepath argument is required')
123+
if args.targets is None:
124+
parser.error('The targets argument is required')
115125

116126

117127
def parse_args(args):

‎tests/main_test.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@
55

66

77
class MainTest(BaseTestCase):
8+
@mock.patch('pyt.__main__.discover_files')
89
@mock.patch('pyt.__main__.parse_args')
910
@mock.patch('pyt.__main__.find_vulnerabilities')
1011
@mock.patch('pyt.__main__.text')
11-
def test_text_output(self, mock_text, mock_find_vulnerabilities, mock_parse_args):
12+
def test_text_output(self, mock_text, mock_find_vulnerabilities, mock_parse_args, mock_discover_files):
1213
mock_find_vulnerabilities.return_value = 'stuff'
1314
example_file = 'examples/vulnerable_code/inter_command_injection.py'
1415
output_file = 'mocked_outfile'
1516

17+
mock_discover_files.return_value = [example_file]
1618
mock_parse_args.return_value = mock.Mock(
1719
autospec=True,
18-
filepath=example_file,
1920
project_root=None,
2021
baseline=None,
2122
json=None,
@@ -32,17 +33,18 @@ def test_text_output(self, mock_text, mock_find_vulnerabilities, mock_parse_args
3233
mock_parse_args.return_value.output_file
3334
)
3435

36+
@mock.patch('pyt.__main__.discover_files')
3537
@mock.patch('pyt.__main__.parse_args')
3638
@mock.patch('pyt.__main__.find_vulnerabilities')
3739
@mock.patch('pyt.__main__.json')
38-
def test_json_output(self, mock_json, mock_find_vulnerabilities, mock_parse_args):
40+
def test_json_output(self, mock_json, mock_find_vulnerabilities, mock_parse_args, mock_discover_files):
3941
mock_find_vulnerabilities.return_value = 'stuff'
4042
example_file = 'examples/vulnerable_code/inter_command_injection.py'
4143
output_file = 'mocked_outfile'
4244

45+
mock_discover_files.return_value = [example_file]
4346
mock_parse_args.return_value = mock.Mock(
4447
autospec=True,
45-
filepath=example_file,
4648
project_root=None,
4749
baseline=None,
4850
json=True,

‎tests/usage_test.py‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ def test_no_args(self):
2525

2626
self.maxDiff = None
2727

28-
EXPECTED = """usage: python -m pyt [-h] [-f FILEPATH] [-a ADAPTOR] [-pr PROJECT_ROOT]
28+
EXPECTED = """usage: python -m pyt [-h] [-a ADAPTOR] [-pr PROJECT_ROOT]
2929
[-b BASELINE_JSON_FILE] [-j] [-m BLACKBOX_MAPPING_FILE]
3030
[-t TRIGGER_WORD_FILE] [-o OUTPUT_FILE] [--ignore-nosec]
31-
[-trim] [-i]
31+
[-r] [-x EXCLUDED_PATHS] [-trim] [-i]
32+
targets [targets ...]
3233
3334
required arguments:
34-
-f FILEPATH, --filepath FILEPATH
35-
Path to the file that should be analysed.
35+
targets source file(s) or directory(s) to be tested
3636
3737
optional arguments:
3838
-a ADAPTOR, --adaptor ADAPTOR
@@ -52,6 +52,9 @@ def test_no_args(self):
5252
-o OUTPUT_FILE, --output OUTPUT_FILE
5353
write report to filename
5454
--ignore-nosec do not skip lines with # nosec comments
55+
-r, --recursive find and process files in subdirectories
56+
-x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS
57+
Separate files with commas
5558
5659
print arguments:
5760
-trim, --trim-reassigned-in
@@ -62,16 +65,17 @@ def test_no_args(self):
6265

6366
self.assertEqual(stdout.getvalue(), EXPECTED)
6467

65-
def test_valid_args_but_no_filepath(self):
68+
def test_valid_args_but_no_targets(self):
6669
with self.assertRaises(SystemExit):
6770
with capture_sys_output() as (_, stderr):
6871
parse_args(['-j'])
6972

70-
EXPECTED = """usage: python -m pyt [-h] [-f FILEPATH] [-a ADAPTOR] [-pr PROJECT_ROOT]
73+
EXPECTED = """usage: python -m pyt [-h] [-a ADAPTOR] [-pr PROJECT_ROOT]
7174
[-b BASELINE_JSON_FILE] [-j] [-m BLACKBOX_MAPPING_FILE]
7275
[-t TRIGGER_WORD_FILE] [-o OUTPUT_FILE] [--ignore-nosec]
73-
[-trim] [-i]
74-
python -m pyt: error: The -f/--filepath argument is required\n"""
76+
[-r] [-x EXCLUDED_PATHS] [-trim] [-i]
77+
targets [targets ...]
78+
python -m pyt: error: the following arguments are required: targets\n"""
7579

7680
self.assertEqual(stderr.getvalue(), EXPECTED)
7781

@@ -89,7 +93,7 @@ def test_valid_args_but_no_filepath(self):
8993

9094
def test_normal_usage(self):
9195
with capture_sys_output() as (stdout, stderr):
92-
parse_args(['-f', 'foo.py'])
96+
parse_args(['foo.py'])
9397

9498
self.assertEqual(stdout.getvalue(), '')
9599
self.assertEqual(stderr.getvalue(), '')

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /