Finish off old sprints

This commit is contained in:
master
2026-02-18 15:01:04 +02:00
parent af4f261de8
commit 1bcab39a2c
28 changed files with 2534 additions and 330 deletions

View File

@@ -280,7 +280,7 @@ public sealed class AstraConnector : IFeedConnector
// Create base provenance record
var baseProvenance = new AdvisoryProvenance(
source: AstraOptions.SourceName,
source: AstraConnectorPlugin.SourceName,
kind: "oval-definition",
value: definition.DefinitionId,
recordedAt: recordedAt,
@@ -379,7 +379,7 @@ public sealed class AstraConnector : IFeedConnector
/// <remarks>
/// Temporary model until full OVAL schema mapping is implemented.
/// </remarks>
internal sealed record AstraVulnerabilityDefinition
public sealed record AstraVulnerabilityDefinition
{
public required string DefinitionId { get; init; }
public required string Title { get; init; }
@@ -393,7 +393,7 @@ internal sealed record AstraVulnerabilityDefinition
/// <summary>
/// Represents an affected package from OVAL test/state elements.
/// </summary>
internal sealed record AstraAffectedPackage
public sealed record AstraAffectedPackage
{
public required string PackageName { get; init; }
public string? MinVersion { get; init; }

View File

@@ -58,6 +58,13 @@ public sealed class OvalParser
return Array.Empty<AstraVulnerabilityDefinition>();
}
// Validate this is an OVAL document by checking root element namespace
if (root.Name.Namespace != OvalDefsNs)
{
throw new OvalParseException(
$"Invalid OVAL document: expected root element in namespace '{OvalDefsNs}', got '{root.Name.Namespace}'");
}
// Extract definitions, tests, objects, and states
var definitions = ExtractDefinitions(root);
var tests = ExtractTests(root);
@@ -91,6 +98,10 @@ public sealed class OvalParser
_logger.LogDebug("Parsed {Count} vulnerability definitions from OVAL XML", results.Count);
return results;
}
catch (OvalParseException)
{
throw; // Re-throw validation exceptions as-is
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to parse OVAL XML");
@@ -281,15 +292,17 @@ public sealed class OvalParser
continue;
}
var evrElement = stateElement.Element(DpkgNs + "evr");
var version = evrElement?.Value ?? string.Empty;
var operation = evrElement?.Attribute("operation")?.Value ?? "less than";
var evrElements = stateElement.Elements(DpkgNs + "evr").ToList();
var constraints = evrElements.Select(evr => new OvalVersionConstraint
{
Version = evr.Value ?? string.Empty,
Operation = evr.Attribute("operation")?.Value ?? "less than"
}).ToList();
states.Add(new OvalState
{
Id = id,
Version = version,
Operation = operation
Constraints = constraints
});
}
@@ -318,17 +331,32 @@ public sealed class OvalParser
string? fixedVersion = null;
string? maxVersion = null;
string? minVersion = null;
if (!string.IsNullOrEmpty(test.StateRef) && stateLookup.TryGetValue(test.StateRef, out var state))
{
// Parse operation to determine if this is a fixed version or affected version range
if (state.Operation.Contains("less than", StringComparison.OrdinalIgnoreCase))
foreach (var constraint in state.Constraints)
{
fixedVersion = state.Version; // Versions less than this are affected
}
else
{
maxVersion = state.Version;
if (constraint.Operation.Contains("less than", StringComparison.OrdinalIgnoreCase) &&
!constraint.Operation.Contains("or equal", StringComparison.OrdinalIgnoreCase))
{
// "less than" -> versions below this are affected; this is the fixed version
fixedVersion = constraint.Version;
}
else if (constraint.Operation.Contains("less than or equal", StringComparison.OrdinalIgnoreCase))
{
// "less than or equal" -> upper bound of affected range
maxVersion = constraint.Version;
}
else if (constraint.Operation.Contains("greater than or equal", StringComparison.OrdinalIgnoreCase))
{
// "greater than or equal" -> lower bound of affected range
minVersion = constraint.Version;
}
else if (constraint.Operation.Contains("greater than", StringComparison.OrdinalIgnoreCase))
{
minVersion = constraint.Version;
}
}
}
@@ -340,7 +368,7 @@ public sealed class OvalParser
PackageName = obj.PackageName,
FixedVersion = fixedVersion,
MaxVersion = maxVersion,
MinVersion = null
MinVersion = minVersion
});
}
}
@@ -377,6 +405,11 @@ public sealed class OvalParser
private sealed record OvalState
{
public required string Id { get; init; }
public required List<OvalVersionConstraint> Constraints { get; init; }
}
private sealed record OvalVersionConstraint
{
public required string Version { get; init; }
public required string Operation { get; init; }
}