@@ -249,6 +249,7 @@ int main() {
249249 TEST_CASE (sarifRuleCoverage);
250250 TEST_CASE (sarifSeverityLevels);
251251 TEST_CASE (sarifNonSecurityRules);
252+ TEST_CASE (sarifInvalidScanfArgTypeGeneric);
252253 }
253254
254255 // Helper to run cppcheck and capture SARIF output
@@ -856,6 +857,83 @@ int main() {
856857
857858 ASSERT_EQUALS (true , foundNonSecurityRule);
858859 }
860+
861+ void sarifInvalidScanfArgTypeGeneric ()
862+ {
863+ // Create a test specifically for invalidScanfArgType_int rule
864+ const std::string scanfTestCode = R"(
865+ #include <cstdio>
866+
867+ int main() {
868+ char str1, str2, str3, str4, str5, str6;
869+
870+ // This should trigger invalidScanfArgType_int errors
871+ // %hhx expects unsigned char* but gets char*
872+ sscanf("12 34 56 78 9A BC", "%hhx %hhx %hhx %hhx %hhx %hhx",
873+ &str1, &str2, &str3, &str4, &str5, &str6);
874+
875+ return 0;
876+ }
877+ )" ;
878+
879+ const std::string sarif = runCppcheckSarif (scanfTestCode);
880+
881+ std::string errorMsg;
882+ ASSERT_EQUALS (true , validateSarifJson (sarif, errorMsg));
883+
884+ // Parse and check for invalidScanfArgType_int rule
885+ picojson::value json;
886+ picojson::parse (json, sarif);
887+ const picojson::object& root = json.get <picojson::object>();
888+ const picojson::array& runs = root.at (" runs" ).get <picojson::array>();
889+ const picojson::object& run = runs[0 ].get <picojson::object>();
890+ const picojson::object& tool = run.at (" tool" ).get <picojson::object>();
891+ const picojson::object& driver = tool.at (" driver" ).get <picojson::object>();
892+ const picojson::array& rules = driver.at (" rules" ).get <picojson::array>();
893+
894+ // Find and verify the invalidScanfArgType_int rule has a genericized description
895+ bool foundRule = false ;
896+
897+ for (const auto & rule : rules)
898+ {
899+ const picojson::object& r = rule.get <picojson::object>();
900+ const std::string ruleId = r.at (" id" ).get <std::string>();
901+
902+ if (ruleId == " invalidScanfArgType_int" )
903+ {
904+ foundRule = true ;
905+
906+ const std::string name = r.at (" name" ).get <std::string>();
907+
908+ // Verify that the rule description is properly genericized
909+ // Should be "Format specifier requires different argument type than provided."
910+ // not "%d in format string (no. 1) requires 'int *' but the argument type is Unknown."
911+ ASSERT_EQUALS (" Format specifier requires different argument type than provided." , name);
912+
913+ break ;
914+ }
915+ }
916+
917+ ASSERT_EQUALS (true , foundRule);
918+
919+ // Verify that results contain the invalidScanfArgType_int rule
920+ const picojson::array& results = run.at (" results" ).get <picojson::array>();
921+ bool foundResult = false ;
922+
923+ for (const auto & result : results)
924+ {
925+ const picojson::object& res = result.get <picojson::object>();
926+ const std::string ruleId = res.at (" ruleId" ).get <std::string>();
927+
928+ if (ruleId == " invalidScanfArgType_int" )
929+ {
930+ foundResult = true ;
931+ break ;
932+ }
933+ }
934+
935+ ASSERT_EQUALS (true , foundResult);
936+ }
859937};
860938
861939REGISTER_TEST (TestSarif)
0 commit comments