Merge branch 'sarif' of https://github.com/bernard-wagner/slither into bernard-wagner-sarif

pull/918/head
Josselin 3 years ago
commit b5080927d5
  1. 21
      slither/__main__.py
  2. 1
      slither/utils/command_line.py
  3. 101
      slither/utils/output.py

@ -24,7 +24,7 @@ from slither.detectors.abstract_detector import AbstractDetector, DetectorClassi
from slither.printers import all_printers from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.output import output_to_json, output_to_zip, ZIP_TYPES_ACCEPTED from slither.utils.output import output_to_json, output_to_zip, output_to_sarif, ZIP_TYPES_ACCEPTED
from slither.utils.output_capture import StandardOutputCapture from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, blue, set_colorization_enabled from slither.utils.colors import red, blue, set_colorization_enabled
from slither.utils.command_line import ( from slither.utils.command_line import (
@ -397,6 +397,13 @@ def parse_args(detector_classes, printer_classes): # pylint: disable=too-many-s
default=defaults_flag_in_config["json"], default=defaults_flag_in_config["json"],
) )
group_misc.add_argument(
"--sarif",
help='Export the results as a SARIF JSON file ("--sarif -" to export to stdout)',
action="store",
default=defaults_flag_in_config["sarif"],
)
group_misc.add_argument( group_misc.add_argument(
"--json-types", "--json-types",
help="Comma-separated list of result types to output to JSON, defaults to " help="Comma-separated list of result types to output to JSON, defaults to "
@ -645,6 +652,8 @@ def main_impl(all_detector_classes, all_printer_classes):
output_error = None output_error = None
outputting_json = args.json is not None outputting_json = args.json is not None
outputting_json_stdout = args.json == "-" outputting_json_stdout = args.json == "-"
outputting_sarif = args.sarif is not None
outputting_sarif_stdout = args.sarif == "-"
outputting_zip = args.zip is not None outputting_zip = args.zip is not None
if args.zip_type not in ZIP_TYPES_ACCEPTED.keys(): if args.zip_type not in ZIP_TYPES_ACCEPTED.keys():
to_log = f'Zip type not accepted, it must be one of {",".join(ZIP_TYPES_ACCEPTED.keys())}' to_log = f'Zip type not accepted, it must be one of {",".join(ZIP_TYPES_ACCEPTED.keys())}'
@ -652,8 +661,8 @@ def main_impl(all_detector_classes, all_printer_classes):
# If we are outputting JSON, capture all standard output. If we are outputting to stdout, we block typical stdout # If we are outputting JSON, capture all standard output. If we are outputting to stdout, we block typical stdout
# output. # output.
if outputting_json: if outputting_json or output_to_sarif:
StandardOutputCapture.enable(outputting_json_stdout) StandardOutputCapture.enable(outputting_json_stdout or outputting_sarif_stdout )
printer_classes = choose_printers(args, all_printer_classes) printer_classes = choose_printers(args, all_printer_classes)
detector_classes = choose_detectors(args, all_detector_classes) detector_classes = choose_detectors(args, all_detector_classes)
@ -732,7 +741,7 @@ def main_impl(all_detector_classes, all_printer_classes):
) = process_all(filename, args, detector_classes, printer_classes) ) = process_all(filename, args, detector_classes, printer_classes)
# Determine if we are outputting JSON # Determine if we are outputting JSON
if outputting_json or outputting_zip: if outputting_json or outputting_zip or output_to_sarif:
# Add our compilation information to JSON # Add our compilation information to JSON
if "compilations" in args.json_types: if "compilations" in args.json_types:
compilation_results = [] compilation_results = []
@ -809,6 +818,10 @@ def main_impl(all_detector_classes, all_printer_classes):
StandardOutputCapture.disable() StandardOutputCapture.disable()
output_to_json(None if outputting_json_stdout else args.json, output_error, json_results) output_to_json(None if outputting_json_stdout else args.json, output_error, json_results)
if outputting_sarif:
StandardOutputCapture.disable()
output_to_sarif(None if outputting_sarif_stdout else args.sarif, json_results)
if outputting_zip: if outputting_zip:
output_to_zip(args.zip, output_error, json_results, args.zip_type) output_to_zip(args.zip, output_error, json_results, args.zip_type)

@ -34,6 +34,7 @@ defaults_flag_in_config = {
"exclude_medium": False, "exclude_medium": False,
"exclude_high": False, "exclude_high": False,
"json": None, "json": None,
"sarif": None,
"json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES), "json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES),
"disable_color": False, "disable_color": False,
"filter_paths": None, "filter_paths": None,

@ -6,6 +6,7 @@ import zipfile
from collections import OrderedDict from collections import OrderedDict
from typing import Optional, Dict, List, Union, Any, TYPE_CHECKING from typing import Optional, Dict, List, Union, Any, TYPE_CHECKING
from zipfile import ZipFile from zipfile import ZipFile
from pkg_resources import require
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
from slither.core.declarations import Contract, Function, Enum, Event, Structure, Pragma from slither.core.declarations import Contract, Function, Enum, Event, Structure, Pragma
@ -56,6 +57,106 @@ def output_to_json(filename: str, error, results: Dict):
json.dump(json_result, f, indent=2) json.dump(json_result, f, indent=2)
def output_to_sarif(filename: str, results: Dict):
"""
:param filename: Filename where the SARIF JSON file will be written. If None or "-", write to stdout
:param error: Error to report
:param results: Results to report
:param logger: Logger where to log potential info
:return:
"""
sarif = {
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {
"driver": {
"name": "Slither",
"informationUri": "https://github.com/crytic/slither",
"version": require("slither-analyzer")[0].version,
"rules": [],
}
},
"results": [],
}
],
}
for detector in results["detectors"]:
check_id = hashlib.sha3_256(detector["check"].encode("utf-8")).hexdigest()
path = detector["first_markdown_element"].split("#")[0]
lines = detector["first_markdown_element"].split("#")[1].split("-")
start_line = int(lines[0][1:])
end_line = start_line
if len(lines) > 1:
end_line = int(lines[1][1:])
confidence = "very-high"
if detector["confidence"] == "Medium":
confidence = "high"
elif detector["confidence"] == "Low":
confidence = "medium"
elif detector["confidence"] == "Informational":
confidence = "low"
risk = "0.0"
if detector["impact"] == "High":
risk = "8.0"
elif detector["impact"] == "Medium":
risk = "4.0"
elif detector["impact"] == "Low":
risk = "3.0"
rule = {
"id": check_id,
"name": detector["check"],
"properties": {"precision": confidence, "security-severity": risk},
}
# Add the rule if does not exist yet
if (
len([x for x in sarif["runs"][0]["tool"]["driver"]["rules"] if x["id"] == check_id])
== 0
): sarif["runs"][0]["tool"]["driver"]["rules"].append(rule)
sarif["runs"][0]["results"].append(
{
"ruleId": check_id,
"message": {"text": detector["description"], "markdown": detector["markdown"]},
"level": "warning",
"locations": [
{
"physicalLocation": {
"artifactLocation": {"uri": path},
"region": {"startLine": start_line, "endLine": end_line},
}
}
],
"partialFingerprints": {"id": detector["id"]},
}
)
if filename == "-":
filename = None
# Determine if we should output to stdout
if filename is None:
# Write json to console
print(json.dumps(sarif))
else:
# Write json to file
if os.path.isfile(filename):
logger.info(yellow(f"{filename} exists already, the overwrite is prevented"))
else:
with open(filename, "w", encoding="utf8") as f:
json.dump(sarif, f, indent=2)
# https://docs.python.org/3/library/zipfile.html#zipfile-objects # https://docs.python.org/3/library/zipfile.html#zipfile-objects
ZIP_TYPES_ACCEPTED = { ZIP_TYPES_ACCEPTED = {
"lzma": zipfile.ZIP_LZMA, "lzma": zipfile.ZIP_LZMA,

Loading…
Cancel
Save