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
};
}
}
}
}