-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moved to cppcheck-backed static analysis
The previous approach used clang static analysis, which is slow and requires a full build of the ledger code. cppcheck provides similar results with a more lightweight approach and is much faster.
- Loading branch information
1 parent
e1dee5d
commit 2ce7566
Showing
7 changed files
with
185 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
from argparse import ArgumentParser | ||
import xml.etree.ElementTree as ET | ||
import os | ||
|
||
|
||
class ErrorRecord: | ||
def __init__(self, id, severity, message, file, line, column): | ||
self.id = id | ||
self.severity = severity | ||
self.message = message | ||
self.file = file | ||
self.line = line | ||
self.column = column | ||
|
||
|
||
def get_error_list(xml_file): | ||
tree = ET.parse(xml_file) | ||
root = tree.getroot() | ||
|
||
error_list = [] | ||
|
||
for error in root.iter('error'): | ||
id = error.get('id') | ||
severity = error.get('severity') | ||
message = error.get('msg') | ||
location = error.find('location') | ||
file = location.get('file') | ||
line = location.get('line') | ||
column = location.get('col') | ||
error_list.append(ErrorRecord(id, severity, message, file, line, column)) | ||
|
||
return error_list | ||
|
||
|
||
def get_report_paths(input_dir): | ||
common_report = os.path.join(input_dir, "common.xml") | ||
signer_report = os.path.join(input_dir, "signer.xml") | ||
ui_report = os.path.join(input_dir, "ui.xml") | ||
tcpsigner_report = os.path.join(input_dir, "tcpsigner.xml") | ||
|
||
if not os.path.isfile(common_report): | ||
raise Exception(f"File not found: {common_report}") | ||
|
||
if not os.path.isfile(signer_report): | ||
raise Exception(f"File not found: {signer_report}") | ||
|
||
if not os.path.isfile(ui_report): | ||
raise Exception(f"File not found: {ui_report}") | ||
|
||
if not os.path.isfile(tcpsigner_report): | ||
raise Exception(f"File not found: {tcpsigner_report}") | ||
|
||
return [common_report, signer_report, ui_report, tcpsigner_report] | ||
|
||
|
||
def generate_report(report, cppcheck_report): | ||
error_list = get_error_list(cppcheck_report) | ||
if len(error_list) == 0: | ||
report.write("### No errors found!\n\n") | ||
else: | ||
report.write(f"### {len(error_list)} errors:\n\n") | ||
report.write("| File | Message | Severity |\n") | ||
report.write("|------|---------|----------|\n") | ||
for error in error_list: | ||
report.write(f"| {error.file}:{error.line} |" | ||
f" {error.message} |" | ||
f" {error.severity} |\n") | ||
|
||
|
||
def main(): | ||
parser = ArgumentParser(description="Generates a report from cppcheck XML outputs") | ||
parser.add_argument( | ||
"-d", | ||
"--inputDir", | ||
dest="input_dir", | ||
help="Directory containing cppcheck XML output files.", | ||
required=True, | ||
) | ||
parser.add_argument( | ||
"-o", | ||
"--output", | ||
dest="output_path", | ||
help="Output file path (Markdown report).", | ||
required=True, | ||
) | ||
options = parser.parse_args() | ||
|
||
try: | ||
[common_report, signer_report, | ||
ui_report, tcpsigner_report] = get_report_paths(options.input_dir) | ||
|
||
# If an old report already exists, remove it | ||
if os.path.exists(options.output_path): | ||
os.remove(options.output_path) | ||
|
||
# Create and open report file | ||
with open(options.output_path, "w") as report: | ||
# Write report title | ||
report.write("# rsk-powhsm Static Analysis Report\n\n") | ||
# Write reports for each module | ||
report.write("## Common\n\n") | ||
generate_report(report, common_report) | ||
report.write("****\n") | ||
report.write("## Signer\n\n") | ||
generate_report(report, signer_report) | ||
report.write("****\n") | ||
report.write("## UI\n\n") | ||
generate_report(report, ui_report) | ||
report.write("****\n") | ||
report.write("## TCPSigner\n\n") | ||
generate_report(report, tcpsigner_report) | ||
|
||
print(f"Report generated at {options.output_path}") | ||
except Exception as e: | ||
print(f"Error: {e}") | ||
exit(1) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/bin/bash | ||
|
||
if [[ $1 == "exec" ]]; then | ||
BASEDIR=$(realpath $(dirname $0)) | ||
SRCDIR=$(realpath $BASEDIR/../src) | ||
REPOROOT=$(realpath $BASEDIR/../..) | ||
|
||
# Remove any existing static analysis data | ||
rm -rf $BASEDIR/output && mkdir $BASEDIR/output | ||
|
||
CCCHECKER=cppcheck | ||
CCCHECKER_FLAGS="--quiet --enable=warning --inline-suppr --error-exitcode=1 --xml --xml-version=2" | ||
|
||
found_errors=0 | ||
# Run static analysis on ledger common | ||
COMMON_SRC_DIR=$SRCDIR/common/src | ||
find $COMMON_SRC_DIR -name "*.[ch]" | \ | ||
xargs ${CCCHECKER} ${CCCHECKER_FLAGS} 2> $BASEDIR/output/common.xml | ||
if [[ $? -ne 0 ]]; then | ||
found_errors=1 | ||
fi | ||
|
||
# Run static analysis on ledger signer | ||
SIGNER_SRC_DIR=$SRCDIR/signer/src | ||
find $SIGNER_SRC_DIR -name "*.[ch]" | \ | ||
egrep -v "(bigdigits|bigdtypes|keccak256)\.[ch]$" | \ | ||
xargs ${CCCHECKER} ${CCCHECKER_FLAGS} 2> $BASEDIR/output/signer.xml | ||
if [[ $? -ne 0 ]]; then | ||
found_errors=1 | ||
fi | ||
|
||
# Run static analysis on ledger ui | ||
UI_SRC_DIR=$SRCDIR/ui/src | ||
find $UI_SRC_DIR -name "*.[ch]" | \ | ||
xargs ${CCCHECKER} ${CCCHECKER_FLAGS} 2> $BASEDIR/output/ui.xml | ||
if [[ $? -ne 0 ]]; then | ||
found_errors=1 | ||
fi | ||
|
||
# Run static analysis on ledger tcpsigner | ||
TCPSIGNER_SRC_DIR=$SRCDIR/tcpsigner | ||
find $TCPSIGNER_SRC_DIR -name "*.[ch]" | \ | ||
egrep -v "(bigdigits|bigdtypes|cJSON)\.[ch]$" | \ | ||
xargs ${CCCHECKER} ${CCCHECKER_FLAGS} 2> $BASEDIR/output/tcpsigner.xml | ||
if [[ $? -ne 0 ]]; then | ||
found_errors=1 | ||
fi | ||
|
||
# If any of the checks above found an error, we want this script to fail to inform the CI. | ||
err_code=$found_errors | ||
else | ||
# Script directory | ||
REPOROOT=$(realpath $(dirname $0)/../..) | ||
SCRIPT=$(realpath $0 --relative-to=$REPOROOT) | ||
|
||
# Generate coverage report | ||
$REPOROOT/docker/mware/do-notty-nousb /hsm2 "./$SCRIPT exec" | ||
err_code=$? | ||
fi | ||
|
||
exit $err_code |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.