3939#include < string>
4040#include < unordered_map>
4141#include < utility>
42+ #include < regex>
4243
4344#include " xml.h"
4445
@@ -190,6 +191,9 @@ ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
190191 attr = errmsg->Attribute (" verbose" );
191192 mVerboseMessage = attr ? attr : " " ;
192193
194+ attr = errmsg->Attribute (" generic" );
195+ mGenericMessage = attr ? attr : " " ;
196+
193197 attr = errmsg->Attribute (" hash" );
194198 hash = attr ? strToInt<std::size_t >(attr) : 0 ;
195199
@@ -216,6 +220,48 @@ ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
216220 }
217221}
218222
223+ // Convert instance-specific messages to generic ones
224+ static std::string makeGeneric (const std::string& message)
225+ {
226+ std::string result = message;
227+
228+ // Replace variable names in single quotes with generic terms
229+ result = std::regex_replace (result, std::regex (R"( Variable '[^']*')" ), " Variable" );
230+ result = std::regex_replace (result, std::regex (R"( variable '[^']*')" ), " variable" );
231+ result = std::regex_replace (result, std::regex (R"( Function '[^']*')" ), " Function" );
232+ result = std::regex_replace (result, std::regex (R"( function '[^']*')" ), " function" );
233+ result = std::regex_replace (result, std::regex (R"( Parameter '[^']*')" ), " Parameter" );
234+ result = std::regex_replace (result, std::regex (R"( parameter '[^']*')" ), " parameter" );
235+ result = std::regex_replace (result, std::regex (R"( Member variable '[^']*')" ), " Member variable" );
236+ result = std::regex_replace (result, std::regex (R"( member variable '[^']*')" ), " member variable" );
237+
238+ // Replace class::member patterns
239+ result = std::regex_replace (result, std::regex (R"( '[^:]*::[^']*')" ), " 'ClassName::member'" );
240+
241+ // Replace specific numbers with generic terms
242+ result = std::regex_replace (result, std::regex (R"( \d+)" ), " N" );
243+
244+ // Replace array access patterns
245+ result = std::regex_replace (result, std::regex (R"( \[[^\]]+\])" ), " [index]" );
246+
247+ // Replace remaining single-quoted identifiers
248+ result = std::regex_replace (result, std::regex (R"( '[a-zA-Z_][a-zA-Z0-9_]*')" ), " 'identifier'" );
249+
250+ // Clean up redundant patterns
251+ result = std::regex_replace (result, std::regex (R"( Variable 'identifier')" ), " Variable" );
252+ result = std::regex_replace (result, std::regex (R"( Function 'identifier')" ), " Function" );
253+ result = std::regex_replace (result, std::regex (R"( Parameter 'identifier')" ), " Parameter" );
254+ result = std::regex_replace (result, std::regex (R"( Member variable 'identifier')" ), " Member variable" );
255+
256+ // Clean up multiple spaces
257+ result = std::regex_replace (result, std::regex (R"( \s+)" ), " " );
258+
259+ // Trim whitespace
260+ result = std::regex_replace (result, std::regex (R"( ^\s+|\s+$)" ), " " );
261+
262+ return result;
263+ }
264+
219265void ErrorMessage::setmsg (const std::string &msg)
220266{
221267 // If a message ends to a '\n' and contains only a one '\n'
@@ -233,12 +279,18 @@ void ErrorMessage::setmsg(const std::string &msg)
233279 if (pos == std::string::npos) {
234280 mShortMessage = replaceStr (msg, " $symbol" , symbolName);
235281 mVerboseMessage = replaceStr (msg, " $symbol" , symbolName);
282+ // Set generic message (remove symbol names and make generic)
283+ const std::string msgWithoutSymbol = replaceStr (msg, " $symbol" , " " );
284+ mGenericMessage = makeGeneric (msgWithoutSymbol);
236285 } else if (startsWith (msg," $symbol:" )) {
237286 mSymbolNames += msg.substr (8 , pos-7 );
238287 setmsg (msg.substr (pos + 1 ));
239288 } else {
240289 mShortMessage = replaceStr (msg.substr (0 , pos), " $symbol" , symbolName);
241290 mVerboseMessage = replaceStr (msg.substr (pos + 1 ), " $symbol" , symbolName);
291+ // Set generic message (remove symbol names and make generic)
292+ const std::string msgWithoutSymbol = replaceStr (msg.substr (0 , pos), " $symbol" , " " );
293+ mGenericMessage = makeGeneric (msgWithoutSymbol);
242294 }
243295}
244296
@@ -289,10 +341,12 @@ std::string ErrorMessage::serialize() const
289341
290342 const std::string saneShortMessage = fixInvalidChars (mShortMessage );
291343 const std::string saneVerboseMessage = fixInvalidChars (mVerboseMessage );
344+ const std::string saneGenericMessage = fixInvalidChars (mGenericMessage );
292345
293346 serializeString (oss, saneShortMessage);
294347 serializeString (oss, saneVerboseMessage);
295348 serializeString (oss, mSymbolNames );
349+ serializeString (oss, saneGenericMessage);
296350 oss += std::to_string (callStack.size ());
297351 oss += " " ;
298352
@@ -320,9 +374,9 @@ void ErrorMessage::deserialize(const std::string &data)
320374 callStack.clear ();
321375
322376 std::istringstream iss (data);
323- std::array<std::string, 10 > results;
377+ std::array<std::string, 11 > results;
324378 std::size_t elem = 0 ;
325- while (iss.good () && elem < 10 ) {
379+ while (iss.good () && elem < 11 ) {
326380 unsigned int len = 0 ;
327381 if (!(iss >> len))
328382 throw InternalError (nullptr , " Internal Error: Deserialization of error message failed - invalid length" );
@@ -348,7 +402,7 @@ void ErrorMessage::deserialize(const std::string &data)
348402 if (!iss.good ())
349403 throw InternalError (nullptr , " Internal Error: Deserialization of error message failed - premature end of data" );
350404
351- if (elem != 10 )
405+ if (elem != 11 )
352406 throw InternalError (nullptr , " Internal Error: Deserialization of error message failed - insufficient elements" );
353407
354408 id = std::move (results[0 ]);
@@ -372,6 +426,7 @@ void ErrorMessage::deserialize(const std::string &data)
372426 mShortMessage = std::move (results[7 ]);
373427 mVerboseMessage = std::move (results[8 ]);
374428 mSymbolNames = std::move (results[9 ]);
429+ mGenericMessage = std::move (results[10 ]);
375430
376431 unsigned int stackSize = 0 ;
377432 if (!(iss >> stackSize))
@@ -497,6 +552,8 @@ std::string ErrorMessage::toXML() const
497552 printer.PushAttribute (" classification" , classification.c_str ());
498553 printer.PushAttribute (" msg" , fixInvalidChars (mShortMessage ).c_str ());
499554 printer.PushAttribute (" verbose" , fixInvalidChars (mVerboseMessage ).c_str ());
555+ if (!mGenericMessage .empty ())
556+ printer.PushAttribute (" generic" , fixInvalidChars (mGenericMessage ).c_str ());
500557 if (cwe.id )
501558 printer.PushAttribute (" cwe" , cwe.id );
502559 if (hash)
0 commit comments