Skip to content
Snippets Groups Projects
Unverified Commit edeabc3f authored by Tim McCormack's avatar Tim McCormack Committed by GitHub
Browse files

Add --summary-format=json option to XSS linter (#25851)

This will simplify updating of the linter thresholds file after XSS linter
violations are addressed.
parent 79370d86
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,7 @@ Tests for main.py
"""
import json
import re
from six import StringIO
from unittest import TestCase
......@@ -69,6 +70,7 @@ class TestXSSLinter(TestCase):
'list_files': False,
'verbose': False,
'rule_totals': False,
'summary_format': 'eslint',
'skip_dirs': ()
},
summary_results=self.summary_results,
......@@ -107,6 +109,7 @@ class TestXSSLinter(TestCase):
'list_files': False,
'verbose': True,
'rule_totals': False,
'summary_format': 'eslint',
'skip_dirs': ()
},
summary_results=self.summary_results,
......@@ -139,6 +142,7 @@ class TestXSSLinter(TestCase):
'list_files': False,
'verbose': False,
'rule_totals': True,
'summary_format': 'eslint',
'skip_dirs': ()
},
summary_results=self.summary_results,
......@@ -153,6 +157,36 @@ class TestXSSLinter(TestCase):
self.assertIsNotNone(re.search(r'{}:\s*{} violations'.format(self.ruleset.python_wrap_html.rule_id, 1), output))
self.assertIsNotNone(re.search(r'{} violations total'.format(5), output))
def test_lint_with_json_output(self):
"""
Tests the top-level linting with JSON summary format.
"""
_lint(
'scripts/xsslint/tests/templates',
template_linters=self.template_linters,
options={
'list_files': False,
'verbose': False,
'rule_totals': True,
'summary_format': 'json',
'skip_dirs': ()
},
summary_results=self.summary_results,
out=self.out,
)
output = self.out.getvalue()
self.assertIsNotNone(re.search(r'test\.py.*{}'.format(self.ruleset.python_wrap_html.rule_id), output))
# Find something that looks like pretty-printed JSON
json_match = re.search(r'\n\{.*\n\}', output, re.DOTALL)
self.assertIsNotNone(json_match)
data = json.loads(json_match.group())
# Check for rule counts (including zero-instance ones) and total
self.assertEqual(1, data['rules']['javascript-concat-html'])
self.assertEqual(0, data['rules']['python-concat-html'])
self.assertEqual(5, data['total'])
def test_lint_with_list_files(self):
"""
Tests the top-level linting with list files option.
......@@ -164,6 +198,7 @@ class TestXSSLinter(TestCase):
'list_files': True,
'verbose': False,
'rule_totals': False,
'summary_format': 'eslint',
'skip_dirs': ()
},
summary_results=self.summary_results,
......
......@@ -151,6 +151,11 @@ def main():
'--rule-totals', dest='rule_totals', action='store_true',
help='Display the totals for each rule.'
)
parser.add_argument(
'--summary-format', dest='summary_format',
choices=['eslint', 'json'], default='eslint',
help='Choose the display format for the summary.'
)
parser.add_argument(
'--verbose', dest='verbose', action='store_true',
help='Print multiple lines where possible for additional context of violations.'
......@@ -166,6 +171,7 @@ def main():
options = {
'list_files': args.list_files,
'rule_totals': args.rule_totals,
'summary_format': args.summary_format,
'verbose': args.verbose,
'skip_dirs': getattr(config, 'SKIP_DIRS', ())
}
......
......@@ -3,6 +3,7 @@ Utility classes for reporting linter results.
"""
import json
import os
import re
......@@ -275,17 +276,43 @@ class SummaryResults(object):
"""
if options['list_files'] is False:
if options['rule_totals']:
max_rule_id_len = max(len(rule_id) for rule_id in self.totals_by_rule)
print("", file=out)
for rule_id in sorted(self.totals_by_rule.keys()):
padding = " " * (max_rule_id_len - len(rule_id))
print("{}: {}{} violations".format(rule_id, padding, self.totals_by_rule[rule_id]), file=out)
print("", file=out)
# matches output of eslint for simplicity
if options['summary_format'] == 'json':
self._print_json_format(options, out)
else:
self._print_eslint_format(options, out)
def _print_eslint_format(self, options, out):
"""
Implementation of print_results with eslint-style output.
"""
if options['rule_totals']:
max_rule_id_len = max(len(rule_id) for rule_id in self.totals_by_rule)
print("", file=out)
print("{} violations total".format(self.total_violations), file=out)
for rule_id in sorted(self.totals_by_rule.keys()):
padding = " " * (max_rule_id_len - len(rule_id))
print("{}: {}{} violations".format(rule_id, padding, self.totals_by_rule[rule_id]), file=out)
print("", file=out)
# matches output of eslint for simplicity
print("", file=out)
print("{} violations total".format(self.total_violations), file=out)
def _print_json_format(self, options, out):
"""
Implementation of print_results with JSON output.
"""
print("", file=out)
print("Violation counts:", file=out)
data = {'rules': self.totals_by_rule}
if options['rule_totals']:
data['total'] = self.total_violations
json.dump(data, fp=out, indent=4, sort_keys=True)
print("", file=out)
print(
"If you've fixed some XSS issues and these numbers have gone down, "
"you can use this to update scripts/xsslint_thresholds.json",
file=out
)
class FileResults(object):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment