Files
git.stella-ops.org/src/StellaOps.Feedser.Source.Distro.Debian/Internal/DebianListParser.cs
Vladimir Moushkov d0c95cf328
Some checks failed
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
UP
2025-10-09 18:59:17 +03:00

108 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
namespace StellaOps.Feedser.Source.Distro.Debian.Internal;
internal static class DebianListParser
{
private static readonly Regex HeaderRegex = new("^\\[(?<date>[^\\]]+)\\]\\s+(?<id>DSA-\\d{4,}-\\d+)\\s+(?<title>.+)$", RegexOptions.Compiled);
private static readonly Regex CveRegex = new("CVE-\\d{4}-\\d{3,7}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static IReadOnlyList<DebianListEntry> Parse(string? content)
{
if (string.IsNullOrWhiteSpace(content))
{
return Array.Empty<DebianListEntry>();
}
var entries = new List<DebianListEntry>();
var currentCves = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
DateTimeOffset currentDate = default;
string? currentId = null;
string? currentTitle = null;
string? currentPackage = null;
foreach (var rawLine in content.Split('\n'))
{
var line = rawLine.TrimEnd('\r');
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
if (line[0] == '[')
{
if (currentId is not null && currentTitle is not null && currentPackage is not null)
{
entries.Add(new DebianListEntry(
currentId,
currentDate,
currentTitle,
currentPackage,
currentCves.Count == 0 ? Array.Empty<string>() : new List<string>(currentCves)));
}
currentCves.Clear();
currentId = null;
currentTitle = null;
currentPackage = null;
var match = HeaderRegex.Match(line);
if (!match.Success)
{
continue;
}
if (!DateTimeOffset.TryParseExact(
match.Groups["date"].Value,
new[] { "dd MMM yyyy", "d MMM yyyy" },
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
out currentDate))
{
continue;
}
currentId = match.Groups["id"].Value.Trim();
currentTitle = match.Groups["title"].Value.Trim();
var separatorIndex = currentTitle.IndexOf(" - ", StringComparison.Ordinal);
currentPackage = separatorIndex > 0
? currentTitle[..separatorIndex].Trim()
: currentTitle.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).FirstOrDefault();
if (string.IsNullOrWhiteSpace(currentPackage))
{
currentPackage = currentId;
}
continue;
}
if (line[0] == '{')
{
foreach (Match match in CveRegex.Matches(line))
{
if (match.Success && !string.IsNullOrWhiteSpace(match.Value))
{
currentCves.Add(match.Value.ToUpperInvariant());
}
}
}
}
if (currentId is not null && currentTitle is not null && currentPackage is not null)
{
entries.Add(new DebianListEntry(
currentId,
currentDate,
currentTitle,
currentPackage,
currentCves.Count == 0 ? Array.Empty<string>() : new List<string>(currentCves)));
}
return entries;
}
}