fix(security): Resolve SonarQube security hotspots
SonarQube Analysis / sonarqube (pull_request) Successful in 3m9s

Introduce a default regex match timeout across the library to prevent potential ReDoS attacks (SonarQube rule S6444).
Implement `[OnDeserialized]` methods to re-establish object invariants and validate state after deserialization, addressing SonarQube rule S5766.
This commit is contained in:
Thom Lamb
2026-05-20 17:19:17 -05:00
parent df1805a402
commit e3153e58c4
26 changed files with 268 additions and 58 deletions
@@ -1,4 +1,5 @@
using System.Collections;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
using Strata.SqlTools.SqlBreakdown.Classes;
@@ -45,6 +46,20 @@ public class ProcedureBreakdown : SqlBreakdownBase
Parameters = parameters ?? new Dictionary<string, string>();
}
/// <summary>
/// Re-establishes invariants after deserialization, since deserialization bypasses the
/// constructors that normally initialize the procedure name and parameter collection
/// (SonarQube rule S5766).
/// </summary>
/// <param name="context">The streaming context for the deserialization operation.</param>
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
RevalidateBreakdownState();
ProcedureName ??= new SqlClause();
Parameters ??= new Dictionary<string, string>();
}
/// <summary>
/// Gets or sets the stored procedure name.
/// </summary>
@@ -169,7 +184,7 @@ public class ProcedureBreakdown : SqlBreakdownBase
// Check if it's an EXEC or EXECUTE statement
var sqlTrimmed = sql.TrimStart();
if (!Regex.IsMatch(sqlTrimmed, @"^\s*(EXEC|EXECUTE)\b",
RegexOptions.IgnoreCase))
RegexOptions.IgnoreCase, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout))
{
errorMessage = "SQL statement must start with EXEC or EXECUTE.";
return false;
@@ -186,7 +201,7 @@ public class ProcedureBreakdown : SqlBreakdownBase
// Pattern: EXEC[UTE] procedureName [@param = value, ...]
var execMatch = Regex.Match(sql,
@"(?:EXEC|EXECUTE)\s+([^\s@,]+)(?:\s+(.*))?$",
RegexOptions.IgnoreCase | RegexOptions.Singleline);
RegexOptions.IgnoreCase | RegexOptions.Singleline, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout);
if (!execMatch.Success)
{
@@ -204,7 +219,7 @@ public class ProcedureBreakdown : SqlBreakdownBase
// Parse parameters - handle both @param = value and positional parameters
var paramMatches = Regex.Matches(parametersText,
@"(@\w+)\s*=\s*([^,]+)(?:,|$)",
RegexOptions.IgnoreCase);
RegexOptions.IgnoreCase, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout);
parameters = paramMatches
.Cast<System.Text.RegularExpressions.Match>()