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 9fb770f

Browse files
bcallerBen Caller
authored and
Ben Caller
committed
Add --only-unsanitised flag to not print sanitised vulnerabilities
It is sometimes what you want, but often you just want the failures without sanitised vulns in the output.
1 parent e78485d commit 9fb770f

File tree

6 files changed

+80
-44
lines changed

6 files changed

+80
-44
lines changed

‎pyt/__main__.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212
get_directory_modules,
1313
get_modules
1414
)
15-
from .formatters import (
16-
json,
17-
text
18-
)
1915
from .usage import parse_args
2016
from .vulnerabilities import (
2117
find_vulnerabilities,
@@ -130,10 +126,7 @@ def main(command_line_args=sys.argv[1:]): # noqa: C901
130126
args.baseline
131127
)
132128

133-
if args.json:
134-
json.report(vulnerabilities, args.output_file)
135-
else:
136-
text.report(vulnerabilities, args.output_file)
129+
args.formatter.report(vulnerabilities, args.output_file, not args.only_unsanitised)
137130

138131
has_unsanitised_vulnerabilities = any(
139132
not isinstance(v, SanitisedVulnerability)

‎pyt/formatters/json.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
"""This formatter outputs the issues in JSON."""
2-
32
import json
43
from datetime import datetime
54

5+
from ..vulnerabilities.vulnerability_helper import SanitisedVulnerability
6+
67

78
def report(
89
vulnerabilities,
9-
fileobj
10+
fileobj,
11+
print_sanitised,
1012
):
1113
"""
1214
Prints issues in JSON format.
@@ -19,7 +21,10 @@ def report(
1921

2022
machine_output = {
2123
'generated_at': time_string,
22-
'vulnerabilities': [vuln.as_dict() for vuln in vulnerabilities]
24+
'vulnerabilities': [
25+
vuln.as_dict() for vuln in vulnerabilities
26+
if print_sanitised or not isinstance(vuln, SanitisedVulnerability)
27+
]
2328
}
2429

2530
result = json.dumps(

‎pyt/formatters/text.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
"""This formatter outputs the issues as plain text."""
2+
from ..vulnerabilities.vulnerability_helper import SanitisedVulnerability
23

34

45
def report(
56
vulnerabilities,
6-
fileobj
7+
fileobj,
8+
print_sanitised,
79
):
810
"""
911
Prints issues in text format.
1012
1113
Args:
1214
vulnerabilities: list of vulnerabilities to report
1315
fileobj: The output file object, which may be sys.stdout
16+
print_sanitised: Print just unsanitised vulnerabilities or sanitised vulnerabilities as well
1417
"""
15-
number_of_vulnerabilities = len(vulnerabilities)
18+
n_vulnerabilities = len(vulnerabilities)
19+
unsanitised_vulnerabilities = [v for v in vulnerabilities if not isinstance(v, SanitisedVulnerability)]
20+
n_unsanitised = len(unsanitised_vulnerabilities)
21+
n_sanitised = n_vulnerabilities - n_unsanitised
22+
heading = "{} vulnerabilit{} found{}{}\n".format(
23+
'No' if n_unsanitised == 0 else n_unsanitised,
24+
'y' if n_unsanitised == 1 else 'ies',
25+
" (plus {} sanitised vulnerabilities)".format(n_sanitised) if n_sanitised else "",
26+
':' if n_vulnerabilities else '.',
27+
)
28+
vulnerabilities_to_print = vulnerabilities if print_sanitised else unsanitised_vulnerabilities
1629
with fileobj:
17-
if number_of_vulnerabilities == 0:
18-
fileobj.write('No vulnerabilities found.\n')
19-
elif number_of_vulnerabilities == 1:
20-
fileobj.write('%s vulnerability found:\n' % number_of_vulnerabilities)
21-
else:
22-
fileobj.write('%s vulnerabilities found:\n' % number_of_vulnerabilities)
30+
fileobj.write(heading)
2331

24-
for i, vulnerability in enumerate(vulnerabilities, start=1):
32+
for i, vulnerability in enumerate(vulnerabilities_to_print, start=1):
2533
fileobj.write('Vulnerability {}:\n{}\n\n'.format(i, vulnerability))

‎pyt/usage.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import sys
44

5+
from .formatters import json, screen, text
6+
57

68
default_blackbox_mapping_file = os.path.join(
79
os.path.dirname(__file__),
@@ -49,12 +51,6 @@ def _add_optional_group(parser):
4951
default=False,
5052
metavar='BASELINE_JSON_FILE',
5153
)
52-
optional_group.add_argument(
53-
'-j', '--json',
54-
help='Prints JSON instead of report.',
55-
action='store_true',
56-
default=False
57-
)
5854
optional_group.add_argument(
5955
'-t', '--trigger-word-file',
6056
help='Input file with a list of sources and sinks',
@@ -115,6 +111,28 @@ def _add_optional_group(parser):
115111
default=True,
116112
dest='allow_local_imports'
117113
)
114+
optional_group.add_argument(
115+
'-u', '--only-unsanitised',
116+
help="Don't print sanitised vulnerabilities.",
117+
action='store_true',
118+
default=False,
119+
)
120+
parser.set_defaults(formatter=text)
121+
formatter_group = optional_group.add_mutually_exclusive_group()
122+
formatter_group.add_argument(
123+
'-j', '--json',
124+
help='Prints JSON instead of report.',
125+
action='store_const',
126+
const=json,
127+
dest='formatter',
128+
)
129+
formatter_group.add_argument(
130+
'-s', '--screen',
131+
help='Prints colorful report.',
132+
action='store_const',
133+
const=screen,
134+
dest='formatter',
135+
)
118136

119137

120138
def parse_args(args):

‎tests/main_test.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,81 @@ class MainTest(BaseTestCase):
88
@mock.patch('pyt.__main__.discover_files')
99
@mock.patch('pyt.__main__.parse_args')
1010
@mock.patch('pyt.__main__.find_vulnerabilities')
11-
@mock.patch('pyt.__main__.text')
11+
@mock.patch('pyt.formatters.text')
1212
def test_text_output(self, mock_text, mock_find_vulnerabilities, mock_parse_args, mock_discover_files):
1313
mock_find_vulnerabilities.return_value = 'stuff'
1414
example_file = 'examples/vulnerable_code/inter_command_injection.py'
1515
output_file = 'mocked_outfile'
1616

17+
import pyt.formatters.text
1718
mock_discover_files.return_value = [example_file]
1819
mock_parse_args.return_value = mock.Mock(
1920
project_root=None,
2021
baseline=None,
21-
json=None,
22-
output_file=output_file
22+
formatter=pyt.formatters.text,
23+
output_file=output_file,
24+
only_unsanitised=False,
2325
)
2426
with self.assertRaises(SystemExit):
2527
main(['parse_args is mocked'])
2628
assert mock_text.report.call_count == 1
2729
mock_text.report.assert_called_with(
2830
mock_find_vulnerabilities.return_value,
29-
mock_parse_args.return_value.output_file
31+
mock_parse_args.return_value.output_file,
32+
True,
3033
)
3134

3235
@mock.patch('pyt.__main__.discover_files')
3336
@mock.patch('pyt.__main__.parse_args')
3437
@mock.patch('pyt.__main__.find_vulnerabilities')
35-
@mock.patch('pyt.__main__.text')
38+
@mock.patch('pyt.formatters.text')
3639
def test_no_vulns_found(self, mock_text, mock_find_vulnerabilities, mock_parse_args, mock_discover_files):
3740
mock_find_vulnerabilities.return_value = []
3841
example_file = 'examples/vulnerable_code/inter_command_injection.py'
3942
output_file = 'mocked_outfile'
4043

44+
import pyt.formatters.text
4145
mock_discover_files.return_value = [example_file]
4246
mock_parse_args.return_value = mock.Mock(
4347
project_root=None,
4448
baseline=None,
45-
json=None,
46-
output_file=output_file
49+
formatter=pyt.formatters.text,
50+
output_file=output_file,
51+
only_unsanitised=True,
4752
)
4853
main(['parse_args is mocked']) # No SystemExit
4954
assert mock_text.report.call_count == 1
5055
mock_text.report.assert_called_with(
5156
mock_find_vulnerabilities.return_value,
52-
mock_parse_args.return_value.output_file
57+
mock_parse_args.return_value.output_file,
58+
False,
5359
)
5460

5561
@mock.patch('pyt.__main__.discover_files')
5662
@mock.patch('pyt.__main__.parse_args')
5763
@mock.patch('pyt.__main__.find_vulnerabilities')
58-
@mock.patch('pyt.__main__.json')
64+
@mock.patch('pyt.formatters.json')
5965
def test_json_output(self, mock_json, mock_find_vulnerabilities, mock_parse_args, mock_discover_files):
6066
mock_find_vulnerabilities.return_value = 'stuff'
6167
example_file = 'examples/vulnerable_code/inter_command_injection.py'
6268
output_file = 'mocked_outfile'
6369

70+
import pyt.formatters.json
6471
mock_discover_files.return_value = [example_file]
6572
mock_parse_args.return_value = mock.Mock(
6673
project_root=None,
6774
baseline=None,
68-
json=True,
69-
output_file=output_file
75+
formatter=pyt.formatters.json,
76+
output_file=output_file,
77+
only_unsanitised=False,
7078
)
7179
with self.assertRaises(SystemExit):
7280
main(['parse_args is mocked'])
7381
assert mock_json.report.call_count == 1
7482
mock_json.report.assert_called_with(
7583
mock_find_vulnerabilities.return_value,
76-
mock_parse_args.return_value.output_file
84+
mock_parse_args.return_value.output_file,
85+
True,
7786
)
7887

7988

‎tests/usage_test.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ def test_no_args(self):
2626
self.maxDiff = None
2727

2828
EXPECTED = """usage: python -m pyt [-h] [-a ADAPTOR] [-pr PROJECT_ROOT]
29-
[-b BASELINE_JSON_FILE] [-j] [-t TRIGGER_WORD_FILE]
29+
[-b BASELINE_JSON_FILE] [-t TRIGGER_WORD_FILE]
3030
[-m BLACKBOX_MAPPING_FILE] [-i] [-o OUTPUT_FILE]
3131
[--ignore-nosec] [-r] [-x EXCLUDED_PATHS]
32-
[--dont-prepend-root] [--no-local-imports]
32+
[--dont-prepend-root] [--no-local-imports] [-u] [-j | -s]
3333
targets [targets ...]
3434
3535
required arguments:
@@ -45,7 +45,6 @@ def test_no_args(self):
4545
-b BASELINE_JSON_FILE, --baseline BASELINE_JSON_FILE
4646
Path of a baseline report to compare against (only
4747
JSON-formatted files are accepted)
48-
-j, --json Prints JSON instead of report.
4948
-t TRIGGER_WORD_FILE, --trigger-word-file TRIGGER_WORD_FILE
5049
Input file with a list of sources and sinks
5150
-m BLACKBOX_MAPPING_FILE, --blackbox-mapping-file BLACKBOX_MAPPING_FILE
@@ -62,7 +61,11 @@ def test_no_args(self):
6261
with app.*
6362
--no-local-imports If set, absolute imports must be relative to the
6463
project root. If not set, modules in the same
65-
directory can be imported just by their names.\n"""
64+
directory can be imported just by their names.
65+
-u, --only-unsanitised
66+
Don't print sanitised vulnerabilities.
67+
-j, --json Prints JSON instead of report.
68+
-s, --screen Prints colorful report.\n"""
6669

6770
self.assertEqual(stdout.getvalue(), EXPECTED)
6871

@@ -72,10 +75,10 @@ def test_valid_args_but_no_targets(self):
7275
parse_args(['-j'])
7376

7477
EXPECTED = """usage: python -m pyt [-h] [-a ADAPTOR] [-pr PROJECT_ROOT]
75-
[-b BASELINE_JSON_FILE] [-j] [-t TRIGGER_WORD_FILE]
78+
[-b BASELINE_JSON_FILE] [-t TRIGGER_WORD_FILE]
7679
[-m BLACKBOX_MAPPING_FILE] [-i] [-o OUTPUT_FILE]
7780
[--ignore-nosec] [-r] [-x EXCLUDED_PATHS]
78-
[--dont-prepend-root] [--no-local-imports]
81+
[--dont-prepend-root] [--no-local-imports] [-u] [-j | -s]
7982
targets [targets ...]
8083
python -m pyt: error: the following arguments are required: targets\n"""
8184

0 commit comments

Comments
(0)

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