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 Strata.SqlTools.SqlBreakdown.Classes;
using Strata.SqlTools.SqlBreakdown.Expressions;
@@ -107,6 +108,28 @@ public class QueryBreakdown : SqlBreakdownBase, IQueryBreakdown
}
}
/// <summary>
/// Re-establishes invariants after deserialization, since deserialization bypasses the
/// constructors that normally initialize the parameter, WITH-clause, and clause backing
/// fields (SonarQube rule S5766).
/// </summary>
/// <param name="context">The streaming context for the deserialization operation.</param>
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
RevalidateBreakdownState();
_parameterList ??= new List<IQueryParam>();
_withClauses ??= new List<IWithClause>();
_selectClause ??= new SqlExpressionClause(splitOnComma: true);
_fromClause ??= new SqlClause();
_whereClause ??= new SqlExpressionClause(splitOnComma: false);
_groupByClause ??= new SqlExpressionClause(splitOnComma: true);
_havingClause ??= new SqlExpressionClause(splitOnComma: false);
_orderByClause ??= new SqlExpressionClause(splitOnComma: true);
_clausesCacheDirty = true;
}
#region Properties
/// <summary>
@@ -1308,7 +1331,7 @@ public class QueryBreakdown : SqlBreakdownBase, IQueryBreakdown
{
// Try to extract position from error message
var match = System.Text.RegularExpressions.Regex.Match(error, @"position[:\s]+(\d+)",
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
System.Text.RegularExpressions.RegexOptions.IgnoreCase, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout);
if (match.Success && int.TryParse(match.Groups[1].Value, out var parsedPos))
{
position = parsedPos;