more audit work
This commit is contained in:
@@ -7,6 +7,8 @@ using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Spdx3.JsonLd;
|
||||
using StellaOps.Spdx3.Model;
|
||||
using StellaOps.Spdx3.Model.Build;
|
||||
using StellaOps.Spdx3.Model.Security;
|
||||
using StellaOps.Spdx3.Model.Software;
|
||||
|
||||
namespace StellaOps.Spdx3;
|
||||
@@ -202,6 +204,19 @@ public sealed class Spdx3Parser : ISpdx3Parser
|
||||
ParseAgent<Spdx3Organization>(element, spdxId),
|
||||
"Tool" or "spdx:Tool" =>
|
||||
ParseAgent<Spdx3Tool>(element, spdxId),
|
||||
"Build" or "build_Build" or "spdx:Build" =>
|
||||
ParseBuild(element, spdxId),
|
||||
"security_Vulnerability" or "Vulnerability" or "spdx:security_Vulnerability" =>
|
||||
ParseVulnerability(element, spdxId),
|
||||
"security_VexAffectedVulnAssessmentRelationship" or
|
||||
"security_VexNotAffectedVulnAssessmentRelationship" or
|
||||
"security_VexFixedVulnAssessmentRelationship" or
|
||||
"security_VexUnderInvestigationVulnAssessmentRelationship" =>
|
||||
ParseVexAssessment(element, spdxId, type),
|
||||
"security_CvssV3VulnAssessmentRelationship" =>
|
||||
ParseCvssAssessment(element, spdxId),
|
||||
"security_EpssVulnAssessmentRelationship" =>
|
||||
ParseEpssAssessment(element, spdxId),
|
||||
_ => ParseGenericElement(element, spdxId, type, warnings)
|
||||
};
|
||||
}
|
||||
@@ -312,6 +327,298 @@ public sealed class Spdx3Parser : ISpdx3Parser
|
||||
};
|
||||
}
|
||||
|
||||
private Spdx3Build ParseBuild(JsonElement element, string spdxId)
|
||||
{
|
||||
// Parse timestamps
|
||||
DateTimeOffset? buildStartTime = null;
|
||||
DateTimeOffset? buildEndTime = null;
|
||||
|
||||
var startTimeStr = GetStringProperty(element, "build_buildStartTime");
|
||||
if (!string.IsNullOrEmpty(startTimeStr) && DateTimeOffset.TryParse(startTimeStr, out var parsedStart))
|
||||
{
|
||||
buildStartTime = parsedStart;
|
||||
}
|
||||
|
||||
var endTimeStr = GetStringProperty(element, "build_buildEndTime");
|
||||
if (!string.IsNullOrEmpty(endTimeStr) && DateTimeOffset.TryParse(endTimeStr, out var parsedEnd))
|
||||
{
|
||||
buildEndTime = parsedEnd;
|
||||
}
|
||||
|
||||
// Parse config source digests
|
||||
var configSourceDigests = ImmutableArray<Spdx3Hash>.Empty;
|
||||
if (element.TryGetProperty("build_configSourceDigest", out var digestsElement) &&
|
||||
digestsElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var digests = new List<Spdx3Hash>();
|
||||
foreach (var digestEl in digestsElement.EnumerateArray())
|
||||
{
|
||||
if (digestEl.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var algorithm = GetStringProperty(digestEl, "algorithm") ?? "sha256";
|
||||
var hashValue = GetStringProperty(digestEl, "hashValue") ?? string.Empty;
|
||||
digests.Add(new Spdx3Hash { Algorithm = algorithm, HashValue = hashValue });
|
||||
}
|
||||
}
|
||||
configSourceDigests = digests.ToImmutableArray();
|
||||
}
|
||||
|
||||
// Parse environment and parameters as dictionaries
|
||||
var environment = ParseDictionary(element, "build_environment");
|
||||
var parameters = ParseDictionary(element, "build_parameter");
|
||||
|
||||
return new Spdx3Build
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = GetStringProperty(element, "@type"),
|
||||
Name = GetStringProperty(element, "name"),
|
||||
Summary = GetStringProperty(element, "summary"),
|
||||
Description = GetStringProperty(element, "description"),
|
||||
BuildType = GetStringProperty(element, "build_buildType") ?? string.Empty,
|
||||
BuildId = GetStringProperty(element, "build_buildId"),
|
||||
BuildStartTime = buildStartTime,
|
||||
BuildEndTime = buildEndTime,
|
||||
ConfigSourceUri = GetStringArrayProperty(element, "build_configSourceUri"),
|
||||
ConfigSourceDigest = configSourceDigests,
|
||||
ConfigSourceEntrypoint = GetStringArrayProperty(element, "build_configSourceEntrypoint"),
|
||||
Environment = environment,
|
||||
Parameter = parameters,
|
||||
VerifiedUsing = ParseIntegrityMethods(element),
|
||||
ExternalRef = ParseExternalRefs(element),
|
||||
ExternalIdentifier = ParseExternalIdentifiers(element)
|
||||
};
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<string, string> ParseDictionary(JsonElement element, string propertyName)
|
||||
{
|
||||
if (!element.TryGetProperty(propertyName, out var dictElement) ||
|
||||
dictElement.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return ImmutableDictionary<string, string>.Empty;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var property in dictElement.EnumerateObject())
|
||||
{
|
||||
if (property.Value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
dict[property.Name] = property.Value.GetString() ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return dict.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
private Spdx3Vulnerability ParseVulnerability(JsonElement element, string spdxId)
|
||||
{
|
||||
DateTimeOffset? publishedTime = null;
|
||||
DateTimeOffset? modifiedTime = null;
|
||||
DateTimeOffset? withdrawnTime = null;
|
||||
|
||||
var publishedStr = GetStringProperty(element, "security_publishedTime");
|
||||
if (!string.IsNullOrEmpty(publishedStr) && DateTimeOffset.TryParse(publishedStr, out var parsedPublished))
|
||||
{
|
||||
publishedTime = parsedPublished;
|
||||
}
|
||||
|
||||
var modifiedStr = GetStringProperty(element, "security_modifiedTime");
|
||||
if (!string.IsNullOrEmpty(modifiedStr) && DateTimeOffset.TryParse(modifiedStr, out var parsedModified))
|
||||
{
|
||||
modifiedTime = parsedModified;
|
||||
}
|
||||
|
||||
var withdrawnStr = GetStringProperty(element, "security_withdrawnTime");
|
||||
if (!string.IsNullOrEmpty(withdrawnStr) && DateTimeOffset.TryParse(withdrawnStr, out var parsedWithdrawn))
|
||||
{
|
||||
withdrawnTime = parsedWithdrawn;
|
||||
}
|
||||
|
||||
return new Spdx3Vulnerability
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = GetStringProperty(element, "@type"),
|
||||
Name = GetStringProperty(element, "name"),
|
||||
Summary = GetStringProperty(element, "summary"),
|
||||
Description = GetStringProperty(element, "description"),
|
||||
PublishedTime = publishedTime,
|
||||
ModifiedTime = modifiedTime,
|
||||
WithdrawnTime = withdrawnTime,
|
||||
ExternalRefs = ParseExternalRefs(element),
|
||||
ExternalIdentifiers = ParseExternalIdentifiers(element)
|
||||
};
|
||||
}
|
||||
|
||||
private Spdx3VulnAssessmentRelationship ParseVexAssessment(
|
||||
JsonElement element,
|
||||
string spdxId,
|
||||
string type)
|
||||
{
|
||||
var assessedElement = GetStringProperty(element, "security_assessedElement") ?? string.Empty;
|
||||
var from = GetStringProperty(element, "from") ?? string.Empty;
|
||||
var toValue = GetStringProperty(element, "to");
|
||||
var toArray = toValue != null ? ImmutableArray.Create(toValue) : GetStringArrayProperty(element, "to");
|
||||
|
||||
DateTimeOffset? publishedTime = null;
|
||||
var publishedStr = GetStringProperty(element, "security_publishedTime");
|
||||
if (!string.IsNullOrEmpty(publishedStr) && DateTimeOffset.TryParse(publishedStr, out var parsedPublished))
|
||||
{
|
||||
publishedTime = parsedPublished;
|
||||
}
|
||||
|
||||
DateTimeOffset? actionStatementTime = null;
|
||||
var actionTimeStr = GetStringProperty(element, "security_actionStatementTime");
|
||||
if (!string.IsNullOrEmpty(actionTimeStr) && DateTimeOffset.TryParse(actionTimeStr, out var parsedActionTime))
|
||||
{
|
||||
actionStatementTime = parsedActionTime;
|
||||
}
|
||||
|
||||
var vexVersion = GetStringProperty(element, "security_vexVersion");
|
||||
var statusNotes = GetStringProperty(element, "security_statusNotes");
|
||||
var actionStatement = GetStringProperty(element, "security_actionStatement");
|
||||
var impactStatement = GetStringProperty(element, "security_impactStatement");
|
||||
var suppliedBy = GetStringProperty(element, "security_suppliedBy");
|
||||
|
||||
// Parse justification for not_affected
|
||||
Spdx3VexJustificationType? justificationType = null;
|
||||
var justificationStr = GetStringProperty(element, "security_justificationType");
|
||||
if (!string.IsNullOrEmpty(justificationStr) &&
|
||||
Enum.TryParse<Spdx3VexJustificationType>(justificationStr, ignoreCase: true, out var parsed))
|
||||
{
|
||||
justificationType = parsed;
|
||||
}
|
||||
|
||||
return type switch
|
||||
{
|
||||
"security_VexAffectedVulnAssessmentRelationship" => new Spdx3VexAffectedVulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = type,
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.Affects,
|
||||
VexVersion = vexVersion,
|
||||
StatusNotes = statusNotes,
|
||||
ActionStatement = actionStatement,
|
||||
ActionStatementTime = actionStatementTime,
|
||||
PublishedTime = publishedTime,
|
||||
SuppliedBy = suppliedBy
|
||||
},
|
||||
"security_VexNotAffectedVulnAssessmentRelationship" => new Spdx3VexNotAffectedVulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = type,
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.DoesNotAffect,
|
||||
VexVersion = vexVersion,
|
||||
StatusNotes = statusNotes,
|
||||
ImpactStatement = impactStatement,
|
||||
JustificationType = justificationType,
|
||||
PublishedTime = publishedTime,
|
||||
SuppliedBy = suppliedBy
|
||||
},
|
||||
"security_VexFixedVulnAssessmentRelationship" => new Spdx3VexFixedVulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = type,
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.FixedIn,
|
||||
VexVersion = vexVersion,
|
||||
StatusNotes = statusNotes,
|
||||
PublishedTime = publishedTime,
|
||||
SuppliedBy = suppliedBy
|
||||
},
|
||||
"security_VexUnderInvestigationVulnAssessmentRelationship" => new Spdx3VexUnderInvestigationVulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = type,
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.UnderInvestigationFor,
|
||||
VexVersion = vexVersion,
|
||||
StatusNotes = statusNotes,
|
||||
PublishedTime = publishedTime,
|
||||
SuppliedBy = suppliedBy
|
||||
},
|
||||
_ => throw new ArgumentException($"Unknown VEX assessment type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
private Spdx3CvssV3VulnAssessmentRelationship ParseCvssAssessment(JsonElement element, string spdxId)
|
||||
{
|
||||
var assessedElement = GetStringProperty(element, "security_assessedElement") ?? string.Empty;
|
||||
var from = GetStringProperty(element, "from") ?? string.Empty;
|
||||
var toValue = GetStringProperty(element, "to");
|
||||
var toArray = toValue != null ? ImmutableArray.Create(toValue) : GetStringArrayProperty(element, "to");
|
||||
|
||||
decimal? baseScore = null;
|
||||
if (element.TryGetProperty("security_score", out var scoreEl) && scoreEl.TryGetDecimal(out var score))
|
||||
{
|
||||
baseScore = score;
|
||||
}
|
||||
|
||||
var vectorString = GetStringProperty(element, "security_vectorString");
|
||||
|
||||
// Parse severity enum
|
||||
Spdx3CvssSeverity? severityEnum = null;
|
||||
var severityStr = GetStringProperty(element, "security_severity");
|
||||
if (!string.IsNullOrEmpty(severityStr) &&
|
||||
Enum.TryParse<Spdx3CvssSeverity>(severityStr, ignoreCase: true, out var parsedSeverity))
|
||||
{
|
||||
severityEnum = parsedSeverity;
|
||||
}
|
||||
|
||||
return new Spdx3CvssV3VulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = "security_CvssV3VulnAssessmentRelationship",
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.HasAssessmentFor,
|
||||
Score = baseScore,
|
||||
VectorString = vectorString,
|
||||
Severity = severityEnum
|
||||
};
|
||||
}
|
||||
|
||||
private Spdx3EpssVulnAssessmentRelationship ParseEpssAssessment(JsonElement element, string spdxId)
|
||||
{
|
||||
var assessedElement = GetStringProperty(element, "security_assessedElement") ?? string.Empty;
|
||||
var from = GetStringProperty(element, "from") ?? string.Empty;
|
||||
var toValue = GetStringProperty(element, "to");
|
||||
var toArray = toValue != null ? ImmutableArray.Create(toValue) : GetStringArrayProperty(element, "to");
|
||||
|
||||
decimal? probability = null;
|
||||
if (element.TryGetProperty("security_probability", out var probEl) && probEl.TryGetDecimal(out var prob))
|
||||
{
|
||||
probability = prob;
|
||||
}
|
||||
|
||||
decimal? percentile = null;
|
||||
if (element.TryGetProperty("security_percentile", out var percEl) && percEl.TryGetDecimal(out var perc))
|
||||
{
|
||||
percentile = perc;
|
||||
}
|
||||
|
||||
return new Spdx3EpssVulnAssessmentRelationship
|
||||
{
|
||||
SpdxId = spdxId,
|
||||
Type = "security_EpssVulnAssessmentRelationship",
|
||||
AssessedElement = assessedElement,
|
||||
From = from,
|
||||
To = toArray,
|
||||
RelationshipType = Spdx3RelationshipType.HasAssessmentFor,
|
||||
Probability = probability,
|
||||
Percentile = percentile
|
||||
};
|
||||
}
|
||||
|
||||
private T ParseAgent<T>(JsonElement element, string spdxId) where T : Spdx3Element
|
||||
{
|
||||
var name = GetStringProperty(element, "name") ?? string.Empty;
|
||||
|
||||
Reference in New Issue
Block a user