// // Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later. // using System.Globalization; using StellaOps.Spdx3.Model.Security; namespace StellaOps.VexLens.Spdx3; /// /// Maps CVSS scores to SPDX 3.0.1 Security profile assessment relationships. /// Sprint: SPRINT_20260107_004_004 Task SP-007 /// public static class CvssMapper { /// /// Maps a CVSS v3 score to an SPDX 3.0.1 CVSS vulnerability assessment relationship. /// /// The CVSS v3 data to map. /// SPDX ID of the vulnerability being assessed. /// SPDX ID of the element being assessed. /// Prefix for generating relationship SPDX IDs. /// The mapped CVSS assessment relationship. public static Spdx3CvssV3VulnAssessmentRelationship MapToSpdx3( CvssV3Data cvssData, string vulnerabilitySpdxId, string assessedElementSpdxId, string spdxIdPrefix) { ArgumentNullException.ThrowIfNull(cvssData); ArgumentException.ThrowIfNullOrWhiteSpace(vulnerabilitySpdxId); ArgumentException.ThrowIfNullOrWhiteSpace(assessedElementSpdxId); ArgumentException.ThrowIfNullOrWhiteSpace(spdxIdPrefix); var spdxId = GenerateSpdxId(spdxIdPrefix, vulnerabilitySpdxId, assessedElementSpdxId, "cvss"); return new Spdx3CvssV3VulnAssessmentRelationship { SpdxId = spdxId, Type = Spdx3CvssV3VulnAssessmentRelationship.TypeName, AssessedElement = assessedElementSpdxId, From = vulnerabilitySpdxId, To = [assessedElementSpdxId], RelationshipType = "hasAssessmentFor", Score = cvssData.BaseScore, Severity = MapSeverity(cvssData.BaseScore), VectorString = cvssData.VectorString, PublishedTime = cvssData.PublishedTime, ModifiedTime = cvssData.ModifiedTime, SuppliedBy = cvssData.Source }; } /// /// Maps an EPSS score to an SPDX 3.0.1 EPSS vulnerability assessment relationship. /// /// The EPSS data to map. /// SPDX ID of the vulnerability being assessed. /// SPDX ID of the element being assessed. /// Prefix for generating relationship SPDX IDs. /// The mapped EPSS assessment relationship. public static Spdx3EpssVulnAssessmentRelationship MapEpssToSpdx3( EpssData epssData, string vulnerabilitySpdxId, string assessedElementSpdxId, string spdxIdPrefix) { ArgumentNullException.ThrowIfNull(epssData); ArgumentException.ThrowIfNullOrWhiteSpace(vulnerabilitySpdxId); ArgumentException.ThrowIfNullOrWhiteSpace(assessedElementSpdxId); ArgumentException.ThrowIfNullOrWhiteSpace(spdxIdPrefix); var spdxId = GenerateSpdxId(spdxIdPrefix, vulnerabilitySpdxId, assessedElementSpdxId, "epss"); return new Spdx3EpssVulnAssessmentRelationship { SpdxId = spdxId, Type = Spdx3EpssVulnAssessmentRelationship.TypeName, AssessedElement = assessedElementSpdxId, From = vulnerabilitySpdxId, To = [assessedElementSpdxId], RelationshipType = "hasAssessmentFor", Probability = epssData.Probability, Percentile = epssData.Percentile, PublishedTime = epssData.ScoreDate, SuppliedBy = "https://www.first.org/epss" }; } /// /// Maps a CVSS v3 base score to severity level. /// /// The CVSS v3 base score (0.0-10.0). /// The severity level. public static Spdx3CvssSeverity MapSeverity(decimal? score) { return score switch { null => Spdx3CvssSeverity.None, 0.0m => Spdx3CvssSeverity.None, >= 0.1m and <= 3.9m => Spdx3CvssSeverity.Low, >= 4.0m and <= 6.9m => Spdx3CvssSeverity.Medium, >= 7.0m and <= 8.9m => Spdx3CvssSeverity.High, >= 9.0m => Spdx3CvssSeverity.Critical, _ => Spdx3CvssSeverity.None }; } /// /// Parses a CVSS v3 vector string to extract component scores. /// /// The CVSS v3 vector string (e.g., "CVSS:3.1/AV:N/AC:L/..."). /// Parsed vector components, or null if parsing fails. public static CvssVectorComponents? ParseVectorString(string? vectorString) { if (string.IsNullOrWhiteSpace(vectorString)) { return null; } try { var parts = vectorString.Split('/'); var components = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var part in parts) { var kv = part.Split(':'); if (kv.Length == 2) { components[kv[0]] = kv[1]; } } return new CvssVectorComponents { Version = components.GetValueOrDefault("CVSS", "3.1"), AttackVector = components.GetValueOrDefault("AV"), AttackComplexity = components.GetValueOrDefault("AC"), PrivilegesRequired = components.GetValueOrDefault("PR"), UserInteraction = components.GetValueOrDefault("UI"), Scope = components.GetValueOrDefault("S"), ConfidentialityImpact = components.GetValueOrDefault("C"), IntegrityImpact = components.GetValueOrDefault("I"), AvailabilityImpact = components.GetValueOrDefault("A") }; } catch { return null; } } private static string GenerateSpdxId( string prefix, string vulnerabilityId, string elementId, string assessmentType) { using var sha = System.Security.Cryptography.SHA256.Create(); var input = $"{vulnerabilityId}:{elementId}:{assessmentType}"; var hash = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input)); var shortHash = Convert.ToHexStringLower(hash)[..12]; return $"{prefix.TrimEnd('/')}/{assessmentType}/{shortHash}"; } } /// /// CVSS v3 data input model. /// Sprint: SPRINT_20260107_004_004 Task SP-007 /// public sealed record CvssV3Data { /// /// Gets or sets the CVSS v3 base score (0.0-10.0). /// public decimal? BaseScore { get; init; } /// /// Gets or sets the CVSS v3 vector string. /// public string? VectorString { get; init; } /// /// Gets or sets the temporal score. /// public decimal? TemporalScore { get; init; } /// /// Gets or sets the environmental score. /// public decimal? EnvironmentalScore { get; init; } /// /// Gets or sets when the score was published. /// public DateTimeOffset? PublishedTime { get; init; } /// /// Gets or sets when the score was modified. /// public DateTimeOffset? ModifiedTime { get; init; } /// /// Gets or sets the source of the CVSS data. /// public string? Source { get; init; } } /// /// EPSS data input model. /// Sprint: SPRINT_20260107_004_004 Task SP-007 /// public sealed record EpssData { /// /// Gets or sets the EPSS probability (0.0-1.0). /// public decimal? Probability { get; init; } /// /// Gets or sets the EPSS percentile (0.0-1.0). /// public decimal? Percentile { get; init; } /// /// Gets or sets the date of the EPSS score. /// public DateTimeOffset? ScoreDate { get; init; } } /// /// Parsed CVSS v3 vector components. /// Sprint: SPRINT_20260107_004_004 Task SP-007 /// public sealed record CvssVectorComponents { /// Gets or sets the CVSS version. public string? Version { get; init; } /// Gets or sets the Attack Vector (AV). public string? AttackVector { get; init; } /// Gets or sets the Attack Complexity (AC). public string? AttackComplexity { get; init; } /// Gets or sets the Privileges Required (PR). public string? PrivilegesRequired { get; init; } /// Gets or sets the User Interaction (UI). public string? UserInteraction { get; init; } /// Gets or sets the Scope (S). public string? Scope { get; init; } /// Gets or sets the Confidentiality Impact (C). public string? ConfidentialityImpact { get; init; } /// Gets or sets the Integrity Impact (I). public string? IntegrityImpact { get; init; } /// Gets or sets the Availability Impact (A). public string? AvailabilityImpact { get; init; } }