fix(game): 收紧 schema 正则校验边界

- 修复 schema 正则校验缺少超时边界导致的 analyzer 风险

- 更新字符串等值比较为 ordinal 语义

- 补充 warning reduction 批处理恢复状态与验证结果
This commit is contained in:
gewuyou 2026-04-29 08:26:19 +08:00
parent 9109eecea9
commit e1c1eb1123
3 changed files with 95 additions and 33 deletions

View File

@ -22,34 +22,42 @@ internal static partial class YamlConfigSchemaValidator
// JS tooling so grouping and backreferences behave consistently across environments.
private const RegexOptions SupportedPatternRegexOptions = RegexOptions.CultureInvariant;
private const string SupportedStringFormatNames = "'date', 'date-time', 'duration', 'email', 'time', 'uri', 'uuid'";
private static readonly TimeSpan SupportedFormatRegexTimeout = TimeSpan.FromSeconds(1);
private static readonly Regex ExactDecimalPattern = new(
@"^(?<sign>[+-]?)(?:(?<integer>\d+)(?:\.(?<fraction>\d*))?|\.(?<fractionOnly>\d+))(?:[eE](?<exponent>[+-]?\d+))?$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedEmailFormatRegex = new(
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedDateFormatRegex = new(
@"^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedDateTimeFormatRegex = new(
@"^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})T(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})(?<fraction>\.\d+)?(?<offset>Z|[+-]\d{2}:\d{2})$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedDurationFormatRegex = new(
@"^P(?:(?<days>\d+)D)?(?:T(?:(?<hours>\d+)H)?(?:(?<minutes>\d+)M)?(?:(?<seconds>\d+(?:\.\d+)?)S)?)?$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedTimeFormatRegex = new(
@"^(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})(?<fraction>\.\d+)?(?<offset>Z|[+-]\d{2}:\d{2})$",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
private static readonly Regex SupportedUriSchemeRegex = new(
@"^[A-Za-z][A-Za-z0-9+\.-]*:",
RegexOptions.CultureInvariant | RegexOptions.Compiled);
RegexOptions.CultureInvariant | RegexOptions.Compiled,
SupportedFormatRegexTimeout);
/// <summary>
/// 从磁盘加载并解析一个 JSON Schema 文件。
@ -1875,7 +1883,7 @@ internal static partial class YamlConfigSchemaValidator
var pattern = patternElement.GetString() ?? string.Empty;
try
{
_ = new Regex(pattern, SupportedPatternRegexOptions);
_ = new Regex(pattern, SupportedPatternRegexOptions, SupportedFormatRegexTimeout);
}
catch (ArgumentException exception)
{
@ -2347,7 +2355,8 @@ internal static partial class YamlConfigSchemaValidator
? null
: new Regex(
pattern,
SupportedPatternRegexOptions),
SupportedPatternRegexOptions,
SupportedFormatRegexTimeout),
formatConstraint);
}
@ -2695,7 +2704,7 @@ internal static partial class YamlConfigSchemaValidator
}
var offset = match.Groups["offset"].Value;
if (offset == "Z")
if (string.Equals(offset, "Z", StringComparison.Ordinal))
{
return true;
}
@ -3287,7 +3296,7 @@ internal static partial class YamlConfigSchemaValidator
}
significand = BigInteger.Parse(digits, CultureInfo.InvariantCulture);
if (match.Groups["sign"].Value == "-")
if (string.Equals(match.Groups["sign"].Value, "-", StringComparison.Ordinal))
{
significand = BigInteger.Negate(significand);
}
@ -3579,7 +3588,9 @@ internal static partial class YamlConfigSchemaValidator
/// <returns>组合后的路径。</returns>
private static string CombineSchemaPath(string parentPath, string propertyName)
{
return parentPath == "<root>" ? propertyName : $"{parentPath}.{propertyName}";
return string.Equals(parentPath, "<root>", StringComparison.Ordinal)
? propertyName
: $"{parentPath}.{propertyName}";
}
/// <summary>

View File

@ -6,33 +6,44 @@
## 当前恢复点
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-092`
- 当前阶段:`Phase 92`
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-093`
- 当前阶段:`Phase 93`
- 当前焦点:
- `2026-04-28` 复核 `PR #300` 最新 open threads代码类线程已与当前工作树对齐仅剩 `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` 的文件计数与验证口径漂移仍然成立
- 已将 tracking 文档修正为与 `6cc87a9...HEAD` 的实际变更规模一致,并与 trace 中记录的 `dotnet build`、定向 `dotnet test``git diff --check` 验证口径保持一致
- `dotnet format --verify-no-changes``GFramework.Core.Tests` 既有 `FINALNEWLINE``CHARSET``WHITESPACE` 基线仍保持独立,不与当前 `ai-plan` 同步修复混提
- `2026-04-29` 使用 `$gframework-batch-boot 50` 从 clean build warning 基线继续分批清理 analyzer warnings
- 已接受三个 worker 的 `GFramework.Cqrs.Tests/Mediator/*` 独立切片,三个 Mediator 测试文件的 warning 已清零
- 主线程补齐 `YamlConfigSchemaValidator` 运行时正则 timeout 与 ordinal 字符串比较,先收掉低风险 `MA0009` / `MA0006`
- 当前停止条件为相对 `origin/main` 接近 `50` 个变更文件;本轮尚未接近阈值,下一批可继续处理 `GFramework.Game/Config/YamlConfigSchemaValidator*`
## 当前活跃事实
- 当前 `origin/main` 基线提交为 `6cc87a9``2026-04-27T20:28:50+08:00`)。
- 当前 `origin/main` 基线提交为 `0e32dab``2026-04-28T17:15:47+08:00`)。
- 当前直接验证结果:
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- `dotnet clean -p:RestoreFallbackFolders= -v:quiet`
- 最新结果:成功;标准 `dotnet clean` 仍会先命中当前 WSL 环境的 Windows NuGet fallback 目录,已按既有环境口径先执行 `dotnet restore GFramework.sln -p:RestoreFallbackFolders= --disable-parallel` 后清理
- `dotnet build -p:RestoreFallbackFolders= -clp:WarningsOnly -v:minimal -m:1 -nodeReuse:false`
- 最新结果:成功;`75` warnings、`0` errorswarning 从本轮基线 `236` 降到 `75`
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 最新结果:成功;`0 Warning(s)``0 Error(s)`
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"`
- 最新结果:成功;`67` 通过、`0` 失败
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Mediator"`
- 最新结果:成功;`45` 通过、`0` 失败
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 最新结果:成功;`75 Warning(s)``0 Error(s)`;剩余均为 `YamlConfigSchemaValidator*``MA0048` / `MA0051`
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"`
- 最新结果:成功;`80` 通过、`0` 失败
- 当前批次摘要:
- 当前分支相对 `6cc87a9...HEAD` 包含 `18` 个已修改文件与 `38` 个新增文件(合计 `56` 个变更文件),分别位于 `GFramework.Core.Tests``GFramework.Cqrs.Tests``GFramework.Core``.agents/skills/gframework-pr-review/``ai-plan/public/analyzer-warning-reduction`
- 本轮没有触碰 `Mediator/*``YamlConfigSchemaValidator*``GFramework.Core.Tests` 的整项目格式基线波次
- 当前已提交分支相对 `origin/main...HEAD` 包含 `3` 个变更文件;本次主线程待提交的 `GFramework.Game/Config/YamlConfigSchemaValidator.cs``ai-plan` 文档会把累计变更推到约 `6` 个文件,低于 `50` 个文件阈值
- 已完成 worker 切片:
- `ed269d4``MediatorArchitectureIntegrationTests.cs`,清理 `MA0048` / `MA0004` / `MA0016`
- `121df44``MediatorAdvancedFeaturesTests.cs`,清理 `MA0048` / `MA0004` / `MA0015`
- `9109eec``MediatorComprehensiveTests.cs`,清理 `MA0048` / `MA0004` / `MA0016` / `MA0002` / `MA0015`
- 主线程切片:`YamlConfigSchemaValidator.cs` 正则 timeout 与 ordinal equality清理 `MA0009` / `MA0006`
## 当前风险
- GitHub PR 上的 open threads 可能仍显示为未关闭,因为当前只同步了 `ai-plan` 文档,尚未推送新的 head 供审查机器人重新折叠线程。
- 缓解措施:推送本次 `ai-plan` 同步提交后重新执行 `$gframework-pr-review`,以最新 head 再核对 thread 状态。
- `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes` 当前会命中项目内大量历史格式诊断。
- 缓解措施:本轮只记录为现存基线,不把 `PR #300` 的 review follow-up 扩展成整项目格式清理。
- `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。
- 缓解措施:继续保持为独立高耦合波次。
- `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,剩余 `45``MA0048``30``MA0051`
- 缓解措施:下一批优先把 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆到独立文件,再评估 `MA0051` 方法拆分。
- 标准 `dotnet clean` 在当前 WSL 环境仍会读取失效的 Windows fallback package folder。
- 缓解措施:本主题验证继续沿用 `-p:RestoreFallbackFolders=`,必要时先执行 solution restore 刷新 Linux 侧资产。
## 活跃文档
@ -53,12 +64,13 @@
## 验证说明
- 权威验证结果统一维护在“当前活跃事实”。
- `GFramework.Core.Tests` 的当前受影响项目 Release 构建已清零,并通过对应定向测试回归。
- `GFramework.Cqrs.Tests` 的当前受影响项目 Release 构建已清零,并通过 Mediator 定向测试回归。
- `GFramework.Game` 当前低风险正则 / 字符串比较切片通过 Release 构建与 config 定向测试;剩余 warning 属于拆文件与复杂度拆分。
- `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。
- warning reduction 的仓库级真值以同轮 `dotnet build`、定向 `dotnet test``git diff --check` 为准,并与 trace 中的验证里程碑保持一致。
## 下一步建议
1. 提交本轮 `PR #300` nitpick follow-up、技能规则更新与 `ai-plan` 同步。
2. 推送后重新执行 `$gframework-pr-review`,确认 `ai-plan` 相关 thread 是否随最新 head 自动收口
3. 若要清理 `dotnet format` 基线,另开 `GFramework.Core.Tests` 格式治理切片,不与当前 PR review 修复混提
1. 提交主线程 `YamlConfigSchemaValidator` 正则安全补丁与本轮 `ai-plan` 同步。
2. 继续下一批 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆文件,目标清理 `MA0048`
3. 再评估 `YamlConfigSchemaValidator.ObjectKeywords.cs` 与主 validator 的 `MA0051` 方法拆分,避免单批触碰过多高耦合逻辑

View File

@ -1,5 +1,44 @@
# Analyzer Warning Reduction 追踪
## 2026-04-29 — RP-093
### 阶段:按 `$gframework-batch-boot 50` 从 clean build warning 基线分批清理
- 触发背景:
- 用户要求先拿构建 warning再分批指派 subagent 加快处理;停止条件解析为分支相对 `origin/main` 接近 `50` 个变更文件
- 基线与环境:
- 当前 `origin/main``0e32dab``2026-04-28T17:15:47+08:00`
- 标准 `dotnet clean` 在当前 WSL 环境仍被 Windows NuGet fallback package folder 阻塞;按既有环境口径先执行 `dotnet restore GFramework.sln -p:RestoreFallbackFolders= --disable-parallel` 后,使用 `-p:RestoreFallbackFolders=` 完成 clean / build
- clean 后 warning 基线:`236` warnings、`0` errors
- 已接受的 worker 范围:
- `ed269d4``GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs`,清理 `MA0048` / `MA0004` / `MA0016`
- `121df44``GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs`,清理 `MA0048` / `MA0004` / `MA0015`
- `9109eec``GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs`,清理 `MA0048` / `MA0004` / `MA0016` / `MA0002` / `MA0015`
- 主线程实施:
- 在 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 为固定格式正则与 schema `pattern` 正则补充 timeout避免运行时正则输入继续触发 `MA0009`
- 将三处字符串等值比较改为 ordinal `string.Equals`,清理 `MA0006`
- 验证里程碑:
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 结果:成功;`0 Warning(s)``0 Error(s)`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Mediator"`
- 结果:成功;`45` 通过、`0` 失败
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false -clp:Summary`
- 结果:成功;`75 Warning(s)``0 Error(s)`
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"`
- 结果:成功;`80` 通过、`0` 失败
- `dotnet clean -p:RestoreFallbackFolders= -v:quiet`
- 结果:成功
- `dotnet build -p:RestoreFallbackFolders= -clp:WarningsOnly -v:minimal -m:1 -nodeReuse:false`
- 结果:成功;`75` warnings、`0` errors
- `git diff --check`
- 结果:成功;无新增 whitespace / conflict-marker 问题
- 当前指标:
- warning 总数:`236` -> `75`
- 剩余 warning 分布:`GFramework.Game/Config/YamlConfigSchemaValidator.cs` `60` 条,`GFramework.Game/Config/YamlConfigSchemaValidator.ObjectKeywords.cs` `15` 条;规则为 `MA0048` `45` 条、`MA0051` `30`
- 已提交分支 diff`3` 个文件;主线程待提交后预计约 `6` 个文件,低于 `50` 个文件阈值
- 下一步:
- 提交主线程 Game / ai-plan 同步后,继续 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆文件,优先清理 `MA0048`
## 2026-04-28 — RP-092
### 阶段:复核 `PR #300` 的 open threads并只修正当前分支仍然成立的 `ai-plan` 漂移