using System.Linq; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using MongoDB.Bson; using StellaOps.Feedser.Models; using StellaOps.Feedser.Source.Ghsa; using StellaOps.Feedser.Source.Common; using StellaOps.Feedser.Source.Ghsa.Internal; using StellaOps.Feedser.Source.Osv.Internal; using StellaOps.Feedser.Source.Osv; using StellaOps.Feedser.Storage.Mongo.Documents; using StellaOps.Feedser.Storage.Mongo.Dtos; var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; var projectRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..")); var fixturesPath = Path.Combine(projectRoot, "src", "StellaOps.Feedser.Source.Osv.Tests", "Fixtures"); RewriteOsvFixtures(fixturesPath); RewriteSnapshotFixtures(fixturesPath); RewriteGhsaFixtures(fixturesPath); return; void RewriteOsvFixtures(string fixturesPath) { var rawPath = Path.Combine(fixturesPath, "osv-ghsa.raw-osv.json"); if (!File.Exists(rawPath)) { Console.WriteLine($"[FixtureUpdater] OSV raw fixture missing: {rawPath}"); return; } using var document = JsonDocument.Parse(File.ReadAllText(rawPath)); var advisories = new List(); foreach (var element in document.RootElement.EnumerateArray()) { var dto = JsonSerializer.Deserialize(element.GetRawText(), serializerOptions); if (dto is null) { continue; } var ecosystem = dto.Affected?.FirstOrDefault()?.Package?.Ecosystem ?? "unknown"; var uri = new Uri($"https://osv.dev/vulnerability/{dto.Id}"); var documentRecord = new DocumentRecord( Guid.NewGuid(), OsvConnectorPlugin.SourceName, uri.ToString(), DateTimeOffset.UtcNow, "fixture-sha", DocumentStatuses.PendingMap, "application/json", null, new Dictionary(StringComparer.Ordinal) { ["osv.ecosystem"] = ecosystem, }, null, DateTimeOffset.UtcNow, null, null); var payload = BsonDocument.Parse(element.GetRawText()); var dtoRecord = new DtoRecord( Guid.NewGuid(), documentRecord.Id, OsvConnectorPlugin.SourceName, "osv.v1", payload, DateTimeOffset.UtcNow); var advisory = OsvMapper.Map(dto, documentRecord, dtoRecord, ecosystem); advisories.Add(advisory); } advisories.Sort((left, right) => string.Compare(left.AdvisoryKey, right.AdvisoryKey, StringComparison.Ordinal)); var snapshot = SnapshotSerializer.ToSnapshot(advisories); File.WriteAllText(Path.Combine(fixturesPath, "osv-ghsa.osv.json"), snapshot); Console.WriteLine($"[FixtureUpdater] Updated {Path.Combine(fixturesPath, "osv-ghsa.osv.json")}"); } void RewriteSnapshotFixtures(string fixturesPath) { var baselinePublished = new DateTimeOffset(2025, 1, 5, 12, 0, 0, TimeSpan.Zero); var baselineModified = new DateTimeOffset(2025, 1, 8, 6, 30, 0, TimeSpan.Zero); var baselineFetched = new DateTimeOffset(2025, 1, 8, 7, 0, 0, TimeSpan.Zero); var cases = new (string Ecosystem, string Purl, string PackageName, string SnapshotFile)[] { ("npm", "pkg:npm/%40scope%2Fleft-pad", "@scope/left-pad", "osv-npm.snapshot.json"), ("PyPI", "pkg:pypi/requests", "requests", "osv-pypi.snapshot.json"), }; foreach (var (ecosystem, purl, packageName, snapshotFile) in cases) { var dto = new OsvVulnerabilityDto { Id = $"OSV-2025-{ecosystem}-0001", Summary = $"{ecosystem} package vulnerability", Details = $"Detailed description for {ecosystem} package {packageName}.", Published = baselinePublished, Modified = baselineModified, Aliases = new[] { $"CVE-2025-11{ecosystem.Length}", $"GHSA-{ecosystem.Length}abc-{ecosystem.Length}def-{ecosystem.Length}ghi" }, Related = new[] { $"OSV-RELATED-{ecosystem}-42" }, References = new[] { new OsvReferenceDto { Url = $"https://example.com/{ecosystem}/advisory", Type = "ADVISORY" }, new OsvReferenceDto { Url = $"https://example.com/{ecosystem}/fix", Type = "FIX" }, }, Severity = new[] { new OsvSeverityDto { Type = "CVSS_V3", Score = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" }, }, Affected = new[] { new OsvAffectedPackageDto { Package = new OsvPackageDto { Ecosystem = ecosystem, Name = packageName, Purl = purl, }, Ranges = new[] { new OsvRangeDto { Type = "SEMVER", Events = new[] { new OsvEventDto { Introduced = "0" }, new OsvEventDto { Fixed = "2.0.0" }, }, }, }, Versions = new[] { "1.0.0", "1.5.0" }, EcosystemSpecific = JsonDocument.Parse("{\"severity\":\"high\"}").RootElement.Clone(), }, }, DatabaseSpecific = JsonDocument.Parse("{\"source\":\"osv.dev\"}").RootElement.Clone(), }; var document = new DocumentRecord( Guid.NewGuid(), OsvConnectorPlugin.SourceName, $"https://osv.dev/vulnerability/{dto.Id}", baselineFetched, "fixture-sha", DocumentStatuses.PendingParse, "application/json", null, new Dictionary(StringComparer.Ordinal) { ["osv.ecosystem"] = ecosystem }, null, baselineModified, null); var payload = BsonDocument.Parse(JsonSerializer.Serialize(dto, serializerOptions)); var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, OsvConnectorPlugin.SourceName, "osv.v1", payload, baselineModified); var advisory = OsvMapper.Map(dto, document, dtoRecord, ecosystem); var snapshot = SnapshotSerializer.ToSnapshot(advisory); File.WriteAllText(Path.Combine(fixturesPath, snapshotFile), snapshot); Console.WriteLine($"[FixtureUpdater] Updated {Path.Combine(fixturesPath, snapshotFile)}"); } } void RewriteGhsaFixtures(string fixturesPath) { var rawPath = Path.Combine(fixturesPath, "osv-ghsa.raw-ghsa.json"); if (!File.Exists(rawPath)) { Console.WriteLine($"[FixtureUpdater] GHSA raw fixture missing: {rawPath}"); return; } JsonDocument document; try { document = JsonDocument.Parse(File.ReadAllText(rawPath)); } catch (JsonException ex) { Console.WriteLine($"[FixtureUpdater] Failed to parse GHSA raw fixture '{rawPath}': {ex.Message}"); return; } using (document) { var advisories = new List(); foreach (var element in document.RootElement.EnumerateArray()) { GhsaRecordDto dto; try { dto = GhsaRecordParser.Parse(Encoding.UTF8.GetBytes(element.GetRawText())); } catch (JsonException) { continue; } var uri = new Uri($"https://github.com/advisories/{dto.GhsaId}"); var documentRecord = new DocumentRecord( Guid.NewGuid(), GhsaConnectorPlugin.SourceName, uri.ToString(), DateTimeOffset.UtcNow, "fixture-sha", DocumentStatuses.PendingMap, "application/json", null, new Dictionary(StringComparer.Ordinal), null, DateTimeOffset.UtcNow, null, null); var advisory = GhsaMapper.Map(dto, documentRecord, DateTimeOffset.UtcNow); advisories.Add(advisory); } advisories.Sort((left, right) => string.Compare(left.AdvisoryKey, right.AdvisoryKey, StringComparison.Ordinal)); var snapshot = SnapshotSerializer.ToSnapshot(advisories); File.WriteAllText(Path.Combine(fixturesPath, "osv-ghsa.ghsa.json"), snapshot); Console.WriteLine($"[FixtureUpdater] Updated {Path.Combine(fixturesPath, "osv-ghsa.ghsa.json")}"); } }