using System.Text.RegularExpressions; using StellaOps.BinaryIndex.Core.Models; using StellaOps.BinaryIndex.FixIndex.Models; namespace StellaOps.BinaryIndex.FixIndex.Parsers; /// /// Parses Alpine APKBUILD secfixes section for CVE fix evidence. /// /// /// APKBUILD secfixes format: /// # secfixes: /// # 1.2.3-r0: /// # - CVE-2024-1234 /// # - CVE-2024-1235 /// public sealed partial class AlpineSecfixesParser : ISecfixesParser { [GeneratedRegex(@"^#\s*secfixes:\s*$", RegexOptions.Compiled | RegexOptions.Multiline)] private static partial Regex SecfixesPatternRegex(); [GeneratedRegex(@"^#\s+(\d+\.\d+[^:]*):$", RegexOptions.Compiled)] private static partial Regex VersionPatternRegex(); [GeneratedRegex(@"^#\s+-\s+(CVE-\d{4}-\d{4,7})$", RegexOptions.Compiled)] private static partial Regex CvePatternRegex(); /// /// Parses APKBUILD secfixes section for version-to-CVE mappings. /// public IEnumerable Parse( string apkbuild, string distro, string release, string sourcePkg) { if (string.IsNullOrWhiteSpace(apkbuild)) yield break; var lines = apkbuild.Split('\n'); var inSecfixes = false; string? currentVersion = null; foreach (var line in lines) { if (SecfixesPatternRegex().IsMatch(line)) { inSecfixes = true; continue; } if (!inSecfixes) continue; // Exit secfixes block on non-comment line if (!line.TrimStart().StartsWith('#')) { inSecfixes = false; continue; } var versionMatch = VersionPatternRegex().Match(line); if (versionMatch.Success) { currentVersion = versionMatch.Groups[1].Value; continue; } var cveMatch = CvePatternRegex().Match(line); if (cveMatch.Success && currentVersion != null) { yield return new FixEvidence { Distro = distro, Release = release, SourcePkg = sourcePkg, CveId = cveMatch.Groups[1].Value, State = FixState.Fixed, FixedVersion = currentVersion, Method = FixMethod.SecurityFeed, // APKBUILD is authoritative Confidence = 0.95m, Evidence = new SecurityFeedEvidence { FeedId = "alpine-secfixes", EntryId = $"{sourcePkg}/{currentVersion}", PublishedAt = DateTimeOffset.UtcNow }, CreatedAt = DateTimeOffset.UtcNow }; } } } }