save checkpoint

This commit is contained in:
master
2026-02-12 21:02:43 +02:00
parent 5bca406787
commit 9911b7d73c
593 changed files with 174390 additions and 1376 deletions

View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
namespace StellaOps.Scanner.Analyzers.OS.Helpers;
public static partial class ChangelogBugReferenceExtractor
{
public static ChangelogBugReferenceExtractionResult Extract(params string?[] changelogInputs)
{
if (changelogInputs is null || changelogInputs.Length == 0)
{
return ChangelogBugReferenceExtractionResult.Empty;
}
var bugReferences = new SortedSet<string>(StringComparer.Ordinal);
var bugToCves = new SortedDictionary<string, SortedSet<string>>(StringComparer.Ordinal);
foreach (var input in changelogInputs)
{
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
foreach (var entry in SplitEntries(input))
{
if (string.IsNullOrWhiteSpace(entry))
{
continue;
}
var cves = ExtractCves(entry);
var bugsInEntry = ExtractBugs(entry);
foreach (var bug in bugsInEntry)
{
bugReferences.Add(bug);
}
if (cves.Count == 0 || bugsInEntry.Count == 0)
{
continue;
}
foreach (var bug in bugsInEntry)
{
if (!bugToCves.TryGetValue(bug, out var mapped))
{
mapped = new SortedSet<string>(StringComparer.Ordinal);
bugToCves[bug] = mapped;
}
mapped.UnionWith(cves);
}
}
}
if (bugReferences.Count == 0)
{
return ChangelogBugReferenceExtractionResult.Empty;
}
var immutableMap = new ReadOnlyDictionary<string, IReadOnlyList<string>>(
bugToCves.ToDictionary(
pair => pair.Key,
pair => (IReadOnlyList<string>)new ReadOnlyCollection<string>(pair.Value.ToArray()),
StringComparer.Ordinal));
return new ChangelogBugReferenceExtractionResult(
new ReadOnlyCollection<string>(bugReferences.ToArray()),
immutableMap);
}
private static IReadOnlyList<string> SplitEntries(string input)
{
var entries = new List<string>();
foreach (var paragraph in EntrySeparatorRegex().Split(input))
{
if (string.IsNullOrWhiteSpace(paragraph))
{
continue;
}
foreach (var line in paragraph.Split('\n'))
{
var trimmed = line.Trim();
if (trimmed.Length == 0)
{
continue;
}
entries.Add(trimmed);
}
}
return entries.Count == 0
? new[] { input.Trim() }
: entries;
}
private static IReadOnlyList<string> ExtractCves(string entry)
{
var cves = new SortedSet<string>(StringComparer.Ordinal);
foreach (Match match in CveRegex().Matches(entry))
{
cves.Add(match.Value.ToUpperInvariant());
}
return cves.ToArray();
}
private static IReadOnlyList<string> ExtractBugs(string entry)
{
var bugs = new SortedSet<string>(StringComparer.Ordinal);
foreach (Match closesMatch in DebianClosesRegex().Matches(entry))
{
foreach (Match idMatch in HashBugIdRegex().Matches(closesMatch.Value))
{
bugs.Add($"debian:#{idMatch.Groups["id"].Value}");
}
}
foreach (Match rhbz in RhbzRegex().Matches(entry))
{
bugs.Add($"rhbz:#{rhbz.Groups["id"].Value}");
}
foreach (Match lp in LaunchpadShortRegex().Matches(entry))
{
bugs.Add($"launchpad:#{lp.Groups["id"].Value}");
}
foreach (Match lp in LaunchpadLongRegex().Matches(entry))
{
bugs.Add($"launchpad:#{lp.Groups["id"].Value}");
}
return bugs.ToArray();
}
[GeneratedRegex(@"(?:\r?\n){2,}", RegexOptions.Compiled)]
private static partial Regex EntrySeparatorRegex();
[GeneratedRegex(@"CVE-\d{4}-\d{4,7}", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex CveRegex();
[GeneratedRegex(@"\bCloses\s*:\s*(?:#\d+[,\s]*)+", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex DebianClosesRegex();
[GeneratedRegex(@"#(?<id>\d+)", RegexOptions.Compiled)]
private static partial Regex HashBugIdRegex();
[GeneratedRegex(@"\bRHBZ\s*#\s*(?<id>\d+)\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex RhbzRegex();
[GeneratedRegex(@"\bLP\s*:\s*#\s*(?<id>\d+)\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex LaunchpadShortRegex();
[GeneratedRegex(@"\bLaunchpad(?:\s+bug)?\s*#\s*(?<id>\d+)\b", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
private static partial Regex LaunchpadLongRegex();
}
public sealed record ChangelogBugReferenceExtractionResult(
IReadOnlyList<string> BugReferences,
IReadOnlyDictionary<string, IReadOnlyList<string>> BugToCves)
{
public static ChangelogBugReferenceExtractionResult Empty { get; } = new(
Array.Empty<string>(),
new ReadOnlyDictionary<string, IReadOnlyList<string>>(
new Dictionary<string, IReadOnlyList<string>>(0, StringComparer.Ordinal)));
public string ToBugReferencesMetadataValue()
=> string.Join(",", BugReferences);
public string ToBugToCvesMetadataValue()
=> string.Join(
";",
BugToCves.Select(static pair => $"{pair.Key}=>{string.Join('|', pair.Value)}"));
}

View File

@@ -4,5 +4,6 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| Task ID | Status | Notes |
| --- | --- | --- |
| QA-SCANNER-VERIFY-010 | DONE | Implemented deterministic changelog bug-id to CVE mapping (`Closes`, `RHBZ`, `LP`) for OS analyzers with Tier 0/1/2 evidence in run-001. |
| REMED-06-SOLID | DOING | SOLID review for OS analyzer files (Tier 0 remediation batch) in progress. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |