Add --summary-format=json option to XSS linter (#25851)
This will simplify updating of the linter thresholds file after XSS linter violations are addressed.
This commit is contained in:
@@ -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)
|
||||
if options['summary_format'] == 'json':
|
||||
self._print_json_format(options, out)
|
||||
else:
|
||||
self._print_eslint_format(options, out)
|
||||
|
||||
# matches output of eslint for simplicity
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user