Initial commit (history squashed)
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build Test Deploy / authority-container (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / docs (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / deploy (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / build-test (push) Has been cancelled
				
			
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		
							
								
								
									
										209
									
								
								src/StellaOps.Feedser.Source.Ghsa/Internal/GhsaMapper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/StellaOps.Feedser.Source.Ghsa/Internal/GhsaMapper.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,209 @@ | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using StellaOps.Feedser.Models; | ||||
| using StellaOps.Feedser.Storage.Mongo.Documents; | ||||
|  | ||||
| namespace StellaOps.Feedser.Source.Ghsa.Internal; | ||||
|  | ||||
| internal static class GhsaMapper | ||||
| { | ||||
|     private static readonly HashSet<string> SemVerEcosystems = new(StringComparer.OrdinalIgnoreCase) | ||||
|     { | ||||
|         "npm", | ||||
|         "maven", | ||||
|         "pip", | ||||
|         "rubygems", | ||||
|         "composer", | ||||
|         "nuget", | ||||
|         "go", | ||||
|         "cargo", | ||||
|     }; | ||||
|  | ||||
|     public static Advisory Map(GhsaRecordDto dto, DocumentRecord document, DateTimeOffset recordedAt) | ||||
|     { | ||||
|         ArgumentNullException.ThrowIfNull(dto); | ||||
|         ArgumentNullException.ThrowIfNull(document); | ||||
|  | ||||
|         var fetchProvenance = new AdvisoryProvenance( | ||||
|             GhsaConnectorPlugin.SourceName, | ||||
|             "document", | ||||
|             document.Uri, | ||||
|             document.FetchedAt, | ||||
|             new[] { ProvenanceFieldMasks.Advisory }); | ||||
|         var mapProvenance = new AdvisoryProvenance( | ||||
|             GhsaConnectorPlugin.SourceName, | ||||
|             "mapping", | ||||
|             dto.GhsaId, | ||||
|             recordedAt, | ||||
|             new[] { ProvenanceFieldMasks.Advisory }); | ||||
|  | ||||
|         var aliases = dto.Aliases | ||||
|             .Where(static alias => !string.IsNullOrWhiteSpace(alias)) | ||||
|             .Distinct(StringComparer.OrdinalIgnoreCase) | ||||
|             .ToArray(); | ||||
|  | ||||
|         var references = dto.References | ||||
|             .Select(reference => CreateReference(reference, recordedAt)) | ||||
|             .Where(static reference => reference is not null) | ||||
|             .Cast<AdvisoryReference>() | ||||
|             .ToList(); | ||||
|  | ||||
|         var affected = CreateAffectedPackages(dto, recordedAt); | ||||
|         var credits = CreateCredits(dto.Credits, recordedAt); | ||||
|  | ||||
|         var severity = dto.Severity?.ToLowerInvariant(); | ||||
|         var summary = dto.Summary ?? dto.Description; | ||||
|  | ||||
|         return new Advisory( | ||||
|             advisoryKey: dto.GhsaId, | ||||
|             title: dto.Summary ?? dto.GhsaId, | ||||
|             summary: summary, | ||||
|             language: "en", | ||||
|             published: dto.PublishedAt, | ||||
|             modified: dto.UpdatedAt ?? dto.PublishedAt, | ||||
|             severity: severity, | ||||
|             exploitKnown: false, | ||||
|             aliases: aliases, | ||||
|             credits: credits, | ||||
|             references: references, | ||||
|             affectedPackages: affected, | ||||
|             cvssMetrics: Array.Empty<CvssMetric>(), | ||||
|             provenance: new[] { fetchProvenance, mapProvenance }); | ||||
|     } | ||||
|  | ||||
|     private static AdvisoryReference? CreateReference(GhsaReferenceDto reference, DateTimeOffset recordedAt) | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(reference.Url) || !Validation.LooksLikeHttpUrl(reference.Url)) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         var kind = reference.Type?.ToLowerInvariant(); | ||||
|  | ||||
|         return new AdvisoryReference( | ||||
|             reference.Url, | ||||
|             kind, | ||||
|             reference.Name, | ||||
|             summary: null, | ||||
|             provenance: new AdvisoryProvenance( | ||||
|                 GhsaConnectorPlugin.SourceName, | ||||
|                 "reference", | ||||
|                 reference.Url, | ||||
|                 recordedAt, | ||||
|                 new[] { ProvenanceFieldMasks.References })); | ||||
|     } | ||||
|  | ||||
|     private static IReadOnlyList<AffectedPackage> CreateAffectedPackages(GhsaRecordDto dto, DateTimeOffset recordedAt) | ||||
|     { | ||||
|         if (dto.Affected.Count == 0) | ||||
|         { | ||||
|             return Array.Empty<AffectedPackage>(); | ||||
|         } | ||||
|  | ||||
|         var packages = new List<AffectedPackage>(dto.Affected.Count); | ||||
|         foreach (var affected in dto.Affected) | ||||
|         { | ||||
|             var ecosystem = string.IsNullOrWhiteSpace(affected.Ecosystem) ? "unknown" : affected.Ecosystem.Trim(); | ||||
|             var packageName = string.IsNullOrWhiteSpace(affected.PackageName) ? "unknown-package" : affected.PackageName.Trim(); | ||||
|             var identifier = $"{ecosystem.ToLowerInvariant()}:{packageName}"; | ||||
|  | ||||
|             var provenance = new[] | ||||
|             { | ||||
|                 new AdvisoryProvenance( | ||||
|                     GhsaConnectorPlugin.SourceName, | ||||
|                     "affected", | ||||
|                     identifier, | ||||
|                     recordedAt, | ||||
|                     new[] { ProvenanceFieldMasks.AffectedPackages }), | ||||
|             }; | ||||
|  | ||||
|             var rangeKind = SemVerEcosystems.Contains(ecosystem) ? "semver" : "vendor"; | ||||
|             var packageType = SemVerEcosystems.Contains(ecosystem) ? AffectedPackageTypes.SemVer : AffectedPackageTypes.Vendor; | ||||
|  | ||||
|             var versionRanges = new List<AffectedVersionRange>(); | ||||
|             if (!string.IsNullOrWhiteSpace(affected.VulnerableRange) || !string.IsNullOrWhiteSpace(affected.PatchedVersion)) | ||||
|             { | ||||
|                 var primitives = new RangePrimitives(null, null, null, new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) | ||||
|                 { | ||||
|                     ["ecosystem"] = ecosystem, | ||||
|                     ["package"] = packageName, | ||||
|                 }); | ||||
|  | ||||
|                 versionRanges.Add(new AffectedVersionRange( | ||||
|                     rangeKind, | ||||
|                     introducedVersion: null, | ||||
|                     fixedVersion: Validation.TrimToNull(affected.PatchedVersion), | ||||
|                     lastAffectedVersion: null, | ||||
|                     rangeExpression: Validation.TrimToNull(affected.VulnerableRange), | ||||
|                     provenance: new AdvisoryProvenance( | ||||
|                         GhsaConnectorPlugin.SourceName, | ||||
|                         "affected-range", | ||||
|                         identifier, | ||||
|                         recordedAt, | ||||
|                         new[] { ProvenanceFieldMasks.VersionRanges }), | ||||
|                     primitives: primitives)); | ||||
|             } | ||||
|  | ||||
|             var statuses = new[] | ||||
|             { | ||||
|                 new AffectedPackageStatus( | ||||
|                     "affected", | ||||
|                     new AdvisoryProvenance( | ||||
|                         GhsaConnectorPlugin.SourceName, | ||||
|                         "affected-status", | ||||
|                         identifier, | ||||
|                         recordedAt, | ||||
|                         new[] { ProvenanceFieldMasks.PackageStatuses })), | ||||
|             }; | ||||
|  | ||||
|             packages.Add(new AffectedPackage( | ||||
|                 packageType, | ||||
|                 identifier, | ||||
|                 platform: null, | ||||
|                 versionRanges: versionRanges, | ||||
|                 statuses: statuses, | ||||
|                 provenance: provenance)); | ||||
|         } | ||||
|  | ||||
|         return packages; | ||||
|     } | ||||
|  | ||||
|     private static IReadOnlyList<AdvisoryCredit> CreateCredits(IReadOnlyList<GhsaCreditDto> credits, DateTimeOffset recordedAt) | ||||
|     { | ||||
|         if (credits.Count == 0) | ||||
|         { | ||||
|             return Array.Empty<AdvisoryCredit>(); | ||||
|         } | ||||
|  | ||||
|         var results = new List<AdvisoryCredit>(credits.Count); | ||||
|         foreach (var credit in credits) | ||||
|         { | ||||
|             var displayName = Validation.TrimToNull(credit.Name) ?? Validation.TrimToNull(credit.Login); | ||||
|             if (displayName is null) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             var contacts = new List<string>(); | ||||
|             if (!string.IsNullOrWhiteSpace(credit.ProfileUrl) && Validation.LooksLikeHttpUrl(credit.ProfileUrl)) | ||||
|             { | ||||
|                 contacts.Add(credit.ProfileUrl.Trim()); | ||||
|             } | ||||
|             else if (!string.IsNullOrWhiteSpace(credit.Login)) | ||||
|             { | ||||
|                 contacts.Add($"https://github.com/{credit.Login.Trim()}"); | ||||
|             } | ||||
|  | ||||
|             var provenance = new AdvisoryProvenance( | ||||
|                 GhsaConnectorPlugin.SourceName, | ||||
|                 "credit", | ||||
|                 displayName, | ||||
|                 recordedAt, | ||||
|                 new[] { ProvenanceFieldMasks.Credits }); | ||||
|  | ||||
|             results.Add(new AdvisoryCredit(displayName, credit.Type, contacts, provenance)); | ||||
|         } | ||||
|  | ||||
|         return results.Count == 0 ? Array.Empty<AdvisoryCredit>() : results; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user