@@ -79,6 +79,7 @@ int main() {
7979 TEST_CASE (sarifSecuritySeverity);
8080 TEST_CASE (sarifLocationInfo);
8181 TEST_CASE (sarifGenericDescriptions);
82+ TEST_CASE (sarifCweTags);
8283 }
8384
8485 // Helper to run cppcheck and capture SARIF output
@@ -323,15 +324,21 @@ int main() {
323324 {
324325 const picojson::array& tags = props.at (" tags" ).get <picojson::array>();
325326 bool hasSecurityTag = false ;
327+ bool hasCweTag = false ;
326328 for (const auto & tag : tags)
327329 {
328- if (tag.get <std::string>() == " security" )
330+ const std::string tagStr = tag.get <std::string>();
331+ if (tagStr == " security" )
329332 {
330333 hasSecurityTag = true ;
331- break ;
334+ }
335+ else if (tagStr.find (" external/cwe/cwe-" ) == 0 )
336+ {
337+ hasCweTag = true ;
332338 }
333339 }
334340 ASSERT_EQUALS (true , hasSecurityTag);
341+ ASSERT_EQUALS (true , hasCweTag);
335342 }
336343 }
337344 }
@@ -425,6 +432,95 @@ int main() {
425432 ASSERT_EQUALS (std::string::npos, fullText.find (" ''" ));
426433 }
427434 }
435+
436+ void sarifCweTags ()
437+ {
438+ const std::string sarif = runCppcheckSarif (testCode);
439+
440+ picojson::value json;
441+ picojson::parse (json, sarif);
442+ const picojson::object& root = json.get <picojson::object>();
443+ const picojson::array& runs = root.at (" runs" ).get <picojson::array>();
444+ const picojson::object& run = runs[0 ].get <picojson::object>();
445+ const picojson::object& tool = run.at (" tool" ).get <picojson::object>();
446+ const picojson::object& driver = tool.at (" driver" ).get <picojson::object>();
447+ const picojson::array& rules = driver.at (" rules" ).get <picojson::array>();
448+
449+ // Check that security-related rules have CWE tags
450+ bool foundNullPointerWithCwe = false ;
451+ bool foundArrayBoundsWithCwe = false ;
452+ bool foundMemleakWithCwe = false ;
453+ bool foundDoubleFreeWithCwe = false ;
454+
455+ for (const auto & rule : rules)
456+ {
457+ const picojson::object& r = rule.get <picojson::object>();
458+ const std::string ruleId = r.at (" id" ).get <std::string>();
459+
460+ // Only check security-related rules that should have CWE tags
461+ if (ruleId == " nullPointer" || ruleId == " arrayIndexOutOfBounds" || ruleId == " doubleFree" ||
462+ ruleId == " memleak" )
463+ {
464+ const picojson::object& props = r.at (" properties" ).get <picojson::object>();
465+
466+ // Verify this rule has security-severity (prerequisite for CWE tags)
467+ if (props.find (" security-severity" ) != props.end ())
468+ {
469+ // Should have tags array
470+ ASSERT (props.find (" tags" ) != props.end ());
471+ const picojson::array& tags = props.at (" tags" ).get <picojson::array>();
472+
473+ bool hasSecurityTag = false ;
474+ bool hasCweTag = false ;
475+ std::string cweTag;
476+
477+ for (const auto & tag : tags)
478+ {
479+ const std::string tagStr = tag.get <std::string>();
480+ if (tagStr == " security" )
481+ {
482+ hasSecurityTag = true ;
483+ }
484+ else if (tagStr.find (" external/cwe/cwe-" ) == 0 )
485+ {
486+ hasCweTag = true ;
487+ cweTag = tagStr;
488+
489+ // Validate CWE tag format: external/cwe/cwe-<number>
490+ ASSERT_EQUALS (0 , tagStr.find (" external/cwe/cwe-" ));
491+ std::string cweNumber = tagStr.substr (17 ); // After "external/cwe/cwe-"
492+ ASSERT (cweNumber.length () > 0 );
493+
494+ // Verify it's a valid number
495+ for (char c : cweNumber)
496+ {
497+ ASSERT (c >= ' 0' && c <= ' 9' );
498+ }
499+
500+ // Track specific CWE mappings we expect
501+ if (ruleId == " nullPointer" && cweNumber == " 476" )
502+ foundNullPointerWithCwe = true ;
503+ else if (ruleId == " arrayIndexOutOfBounds" && cweNumber == " 788" )
504+ foundArrayBoundsWithCwe = true ;
505+ else if (ruleId == " memleak" && cweNumber == " 401" )
506+ foundMemleakWithCwe = true ;
507+ else if (ruleId == " doubleFree" && cweNumber == " 415" )
508+ foundDoubleFreeWithCwe = true ;
509+ }
510+ }
511+
512+ ASSERT_EQUALS (true , hasSecurityTag);
513+ ASSERT_EQUALS (true , hasCweTag);
514+ }
515+ }
516+ }
517+
518+ // Verify we found at least some of the expected CWE mappings
519+ // Note: Not all may be present depending on what the test code triggers
520+ bool foundAnyCweMapping =
521+ foundNullPointerWithCwe || foundArrayBoundsWithCwe || foundMemleakWithCwe || foundDoubleFreeWithCwe;
522+ ASSERT_EQUALS (true , foundAnyCweMapping);
523+ }
428524};
429525
430526REGISTER_TEST (TestSarif)
0 commit comments