Skip to content

Fix first instance rule being used as rule description for all violations of that rule and other SARIF improvements #7640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 70 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
793e0a6
add description for sarif based on id so github doesnt show same text…
Nettozx Jul 1, 2025
152f804
better description handling and add rule.name to serialize
Nettozx Jul 1, 2025
f68a78e
only set severity level for security related sarif findings
Nettozx Jul 1, 2025
348abc7
set problem severity for non security findings
Nettozx Jul 1, 2025
11d537a
Merge branch 'danmar:main' into main
Nettozx Jul 1, 2025
ad9703a
no prefix string, and always set problem severity
Nettozx Jul 1, 2025
6c231a6
Merge branch 'main' of github.com:Nettozx/cppcheck
Nettozx Jul 1, 2025
49f64df
set defaultConfiguration to the same severity level
Nettozx Jul 1, 2025
86846fc
oops it was already there
Nettozx Jul 2, 2025
af080f2
guess recommendation is not valid even though the github documentatio…
Nettozx Jul 2, 2025
ed9b0ae
security-severity needs to be a string
Nettozx Jul 2, 2025
ce9c164
try short message for name
Nettozx Jul 2, 2025
069b0dd
update description functions to fallback to values from finding. add …
Nettozx Jul 2, 2025
1ddf844
change name back to short desc, change short desc to shortMessage, up…
Nettozx Jul 2, 2025
428f2ef
revert the shortDescription value, that causes the original issue to …
Nettozx Jul 2, 2025
50575b0
update comment
Nettozx Jul 2, 2025
c19bca9
more comment updates
Nettozx Jul 2, 2025
c1c4e90
add to authors
Nettozx Jul 2, 2025
a0da1e0
braces
Nettozx Jul 2, 2025
de9a4bc
unit tests
Nettozx Jul 2, 2025
e46a4fb
remove duplicate code
Nettozx Jul 2, 2025
c1d8f9b
match sarifSeverity for security-severity levels
Nettozx Jul 2, 2025
b43ac8f
fix misconception about isCriticalErrorId()
Nettozx Jul 2, 2025
8c9e3a9
update unit test
Nettozx Jul 2, 2025
ef874d3
test generic message builder
Nettozx Jul 2, 2025
cc301a5
Merge branch 'danmar:main' into generic
Nettozx Jul 2, 2025
346587a
add more regex to handle empty qutoes and extra spaces
Nettozx Jul 2, 2025
692c237
formatting
Nettozx Jul 2, 2025
5461bf1
add more pattern recognition for generification output. update messag…
Nettozx Jul 2, 2025
e676f87
uncrustify downloaded from link has _f suffix, update DETECTED_VERSIO…
Nettozx Jul 2, 2025
9a402a8
added unit tests for sarif and ran uncrustify
Nettozx Jul 2, 2025
2d15a78
add cwe tags
Nettozx Jul 2, 2025
f941ec9
add tests for cwe tags
Nettozx Jul 2, 2025
eb23dbe
fix regex issues for repeated varnames and empty brackets. fix issue …
Nettozx Jul 2, 2025
af98d3b
add more sarif test cases
Nettozx Jul 2, 2025
6b29143
fix issue for invalidScanfArgType_int output not being generic
Nettozx Jul 2, 2025
dff042a
fix scanf regex and add unit test for it
Nettozx Jul 2, 2025
47cec25
remove ruleID specific pattern matching and define generic regex that…
Nettozx Jul 2, 2025
235f950
fix column number being 0 issue
Nettozx Jul 3, 2025
35c575a
allow cwe tags for all rules and not just security related
Nettozx Jul 3, 2025
58df85f
more regex patterns to cover more instance specific data coming throu…
Nettozx Jul 3, 2025
2b64d7a
make description getters static
Nettozx Jul 3, 2025
5f2195e
Merge branch 'main' into main
Nettozx Jul 15, 2025
93c3a8f
move logic to errorlogger and make generic member
Nettozx Jul 15, 2025
dba842a
more regex and cleanup duplicate logic
Nettozx Jul 15, 2025
6c2829d
make it more simpler, remove generic for xml, update tests
Nettozx Jul 15, 2025
c978499
run uncrustify
Nettozx Jul 15, 2025
ee36017
Merge pull request #1 from Nettozx/generic
Nettozx Jul 15, 2025
aaea761
just make everything empty strings because github will then default t…
Nettozx Jul 16, 2025
754e509
dont need generic message anymore
Nettozx Jul 17, 2025
bab310b
revert error logger tests
Nettozx Jul 17, 2025
2e721e9
remove irrelevant tests
Nettozx Jul 17, 2025
cc9702c
add test case to check instance specific error messages
Nettozx Jul 17, 2025
cf78418
run uncrustify
Nettozx Jul 17, 2025
f52bce4
add description
Nettozx Jul 17, 2025
928d988
cross platform approach to running tests copied from cppcheckexecutor
Nettozx Jul 17, 2025
c973c26
run uncrustify
Nettozx Jul 17, 2025
e789c06
Merge branch 'generic'
Nettozx Jul 17, 2025
717d459
remove any unneccessary changes
Nettozx Jul 17, 2025
6e8bf04
revert formatting changes made by uncrustify. use ss instead of to_st…
Nettozx Jul 17, 2025
a1562fa
fix selfcheck issues
Nettozx Jul 17, 2025
143e2c8
add attempts at other executable paths
Nettozx Jul 17, 2025
47749d2
fix clang-tidy issues
Nettozx Jul 17, 2025
50b7c22
few more braces
Nettozx Jul 17, 2025
88854d4
revert a few more spacing
Nettozx Jul 17, 2025
ac402ff
cmake executable path in test, and clang-tidy fixes
Nettozx Jul 18, 2025
c1be953
empty spaces
Nettozx Jul 18, 2025
f2ab096
fix selfcheck issues
Nettozx Jul 18, 2025
74999d5
fix clang-tidy issues
Nettozx Jul 18, 2025
deb707f
Merge branch 'main' into main
Nettozx Jul 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ Tomo Dote
Toralf Förster
Troshin V.S.
Tyson Nottingham
Usman Majid
Valentin Batz
Valerii Lashmanov
Vasily Maslyukov
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ ifndef INCLUDE_FOR_CLI
endif

ifndef INCLUDE_FOR_TEST
INCLUDE_FOR_TEST=-Ilib -Ifrontend -Icli -isystem externals/simplecpp -isystem externals/tinyxml2
INCLUDE_FOR_TEST=-Ilib -Ifrontend -Icli -isystem externals/simplecpp -isystem externals/tinyxml2 -isystem externals/picojson
endif

BIN=$(DESTDIR)$(PREFIX)/bin
Expand Down Expand Up @@ -316,6 +316,7 @@ TESTOBJ = test/fixture.o \
test/testpreprocessor.o \
test/testprocessexecutor.o \
test/testprogrammemory.o \
test/testsarif.o \
test/testsettings.o \
test/testsimplifytemplate.o \
test/testsimplifytokens.o \
Expand Down Expand Up @@ -834,6 +835,9 @@ test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/proc
test/testprogrammemory.o: test/testprogrammemory.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testprogrammemory.cpp

test/testsarif.o: test/testsarif.cpp lib/config.h lib/json.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsarif.cpp

test/testsettings.o: test/testsettings.cpp lib/addoninfo.h lib/check.h lib/checkers.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsettings.cpp

Expand Down
81 changes: 58 additions & 23 deletions cli/cppcheckexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,33 +92,65 @@ namespace {
if (finding.callStack.empty())
continue;
if (ruleIds.insert(finding.id).second) {
// setting name and description to empty strings will make github default
// to the instance specific violation message and not rule description,
// this makes it so not all the violations have the same description.
picojson::object rule;
rule["id"] = picojson::value(finding.id);
// rule.name
rule["name"] = picojson::value("");
// rule.shortDescription.text
picojson::object shortDescription;
shortDescription["text"] = picojson::value(finding.shortMessage());
shortDescription["text"] = picojson::value("");
rule["shortDescription"] = picojson::value(shortDescription);
// rule.fullDescription.text
picojson::object fullDescription;
fullDescription["text"] = picojson::value(finding.verboseMessage());
fullDescription["text"] = picojson::value("");
rule["fullDescription"] = picojson::value(fullDescription);
// rule.help.text
picojson::object help;
help["text"] = picojson::value(finding.verboseMessage()); // FIXME provide proper help text
help["text"] = picojson::value("");
rule["help"] = picojson::value(help);
// rule.properties.precision, rule.properties.problem.severity
picojson::object properties;
properties["precision"] = picojson::value(sarifPrecision(finding));
const char* securitySeverity = nullptr;
if (finding.severity == Severity::error && !ErrorLogger::isCriticalErrorId(finding.id))
securitySeverity = "9.9"; // We see undefined behavior
//else if (finding.severity == Severity::warning)
// securitySeverity = 5.1; // We see potential undefined behavior
if (securitySeverity) {
properties["security-severity"] = picojson::value(securitySeverity);
const picojson::array tags{picojson::value("security")};
// rule.properties.security-severity, rule.properties.tags
picojson::array tags;

// If we have a CWE ID, treat it as security-related (CWE is the authoritative source for security weaknesses)
if (finding.cwe.id > 0) {
double securitySeverity = 0;
if (finding.severity == Severity::error && !ErrorLogger::isCriticalErrorId(finding.id)) {
securitySeverity = 9.9; // critical = 9.0+
}
else if (finding.severity == Severity::warning) {
securitySeverity = 8.5; // high = 7.0 to 8.9
}
else if (finding.severity == Severity::performance || finding.severity == Severity::portability ||
finding.severity == Severity::style) {
securitySeverity = 5.5; // medium = 4.0 to 6.9
}
else if (finding.severity == Severity::information || finding.severity == Severity::internal ||
finding.severity == Severity::debug || finding.severity == Severity::none) {
securitySeverity = 2.0; // low = 0.1 to 3.9
}
if (securitySeverity > 0.0) {
std::ostringstream ss;
ss << securitySeverity;
properties["security-severity"] = picojson::value(ss.str());
tags.emplace_back("external/cwe/cwe-" + std::to_string(finding.cwe.id));
tags.emplace_back("security");
}
}

// Add tags array if it has any content
if (!tags.empty()) {
properties["tags"] = picojson::value(tags);
}

// Set problem.severity for use with github
const std::string problemSeverity = sarifSeverity(finding);
properties["problem.severity"] = picojson::value(problemSeverity);
rule["properties"] = picojson::value(properties);
// rule.defaultConfiguration.level
picojson::object defaultConfiguration;
Expand Down Expand Up @@ -198,14 +230,15 @@ namespace {

return picojson::value(doc).serialize(true);
}
private:

private:
static std::string sarifSeverity(const ErrorMessage& errmsg) {
if (ErrorLogger::isCriticalErrorId(errmsg.id))
return "error";
switch (errmsg.severity) {
case Severity::error:
case Severity::warning:
return "error";
case Severity::style:
case Severity::portability:
case Severity::performance:
Expand Down Expand Up @@ -672,19 +705,21 @@ void StdLogger::reportErr(const ErrorMessage &msg)
mGuidelineMapping, msgCopy.severity);
msgCopy.classification = getClassification(msgCopy.guideline, mSettings.reportType);

// TODO: there should be no need for verbose and default messages here
const std::string msgStr = msgCopy.toString(mSettings.verbose, mSettings.templateFormat, mSettings.templateLocation);
if (mSettings.outputFormat == Settings::OutputFormat::sarif) {
mSarifReport.addFinding(std::move(msgCopy));
} else {
// TODO: there should be no need for verbose and default messages here
const std::string msgStr = msgCopy.toString(mSettings.verbose, mSettings.templateFormat, mSettings.templateLocation);

// Alert only about unique errors
if (!mSettings.emitDuplicates && !mShownErrors.insert(msgStr).second)
return;
// Alert only about unique errors
if (!mSettings.emitDuplicates && !mShownErrors.insert(msgStr).second)
return;

if (mSettings.outputFormat == Settings::OutputFormat::sarif)
mSarifReport.addFinding(std::move(msgCopy));
else if (mSettings.outputFormat == Settings::OutputFormat::xml)
reportErr(msgCopy.toXML());
else
reportErr(msgStr);
if (mSettings.outputFormat == Settings::OutputFormat::xml)
reportErr(msgCopy.toXML());
else
reportErr(msgStr);
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions runformat
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
UNCRUSTIFY_VERSION="0.72.0"
UNCRUSTIFY="${UNCRUSTIFY-uncrustify}"

DETECTED_VERSION=$("$UNCRUSTIFY" --version 2>&1 | grep -o -E '[0-9.]+')
if [ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]; then
DETECTED_VERSION=$("$UNCRUSTIFY" --version 2>&1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+')
if [[ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]]; then
echo "You should use version: ${UNCRUSTIFY_VERSION}"
echo "Detected version: ${DETECTED_VERSION}"
exit 1
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ if (BUILD_TESTS)
target_include_directories(testrunner SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS})
endif()
target_externals_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/)
target_externals_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/picojson/)
if (Boost_FOUND)
target_include_directories(testrunner SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
endif()
Expand Down
3 changes: 3 additions & 0 deletions test/cli/helloworld_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ def test_sarif():
assert 'security' in res['runs'][0]['tool']['driver']['rules'][0]['properties']['tags']
assert re.match(r'[0-9]+(.[0-9]+)+', res['runs'][0]['tool']['driver']['semanticVersion'])
assert 'level' in res['runs'][0]['tool']['driver']['rules'][0]['defaultConfiguration'] # #13885
assert res['runs'][0]['tool']['driver']['rules'][0]['shortDescription']['text'] == ''
assert res['runs'][0]['results'][0]['message']['text'] == 'Division by zero.'
assert res['runs'][0]['tool']['driver']['rules'][0]['properties']['problem.severity'] == 'error'


def test_xml_checkers_report():
Expand Down
Loading