using System.Collections.Immutable; using System.Linq; using System.Globalization; using System.Text.Json; namespace StellaOps.Feedser.Source.Ru.Nkcki.Internal; internal static class RuNkckiJsonParser { public static RuNkckiVulnerabilityDto Parse(JsonElement element) { var fstecId = element.TryGetProperty("vuln_id", out var vulnIdElement) && vulnIdElement.TryGetProperty("FSTEC", out var fstec) ? Normalize(fstec.GetString()) : null; var mitreId = element.TryGetProperty("vuln_id", out vulnIdElement) && vulnIdElement.TryGetProperty("MITRE", out var mitre) ? Normalize(mitre.GetString()) : null; var datePublished = ParseDate(element.TryGetProperty("date_published", out var published) ? published.GetString() : null); var dateUpdated = ParseDate(element.TryGetProperty("date_updated", out var updated) ? updated.GetString() : null); var cvssRating = Normalize(element.TryGetProperty("cvss_rating", out var rating) ? rating.GetString() : null); bool? patchAvailable = element.TryGetProperty("patch_available", out var patch) ? patch.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, _ => null, } : null; var description = Normalize(element.TryGetProperty("description", out var desc) ? desc.GetString() : null); var mitigation = Normalize(element.TryGetProperty("mitigation", out var mitigationElement) ? mitigationElement.GetString() : null); var productCategory = Normalize(element.TryGetProperty("product_category", out var category) ? category.GetString() : null); var impact = Normalize(element.TryGetProperty("impact", out var impactElement) ? impactElement.GetString() : null); var method = Normalize(element.TryGetProperty("method_of_exploitation", out var methodElement) ? methodElement.GetString() : null); bool? userInteraction = element.TryGetProperty("user_interaction", out var uiElement) ? uiElement.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, _ => null, } : null; string? softwareText = null; bool? softwareHasCpe = null; if (element.TryGetProperty("vulnerable_software", out var softwareElement)) { if (softwareElement.TryGetProperty("software_text", out var textElement)) { softwareText = Normalize(textElement.GetString()?.Replace('\r', ' ')); } if (softwareElement.TryGetProperty("cpe", out var cpeElement)) { softwareHasCpe = cpeElement.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, _ => null, }; } } RuNkckiCweDto? cweDto = null; if (element.TryGetProperty("cwe", out var cweElement)) { int? number = null; if (cweElement.TryGetProperty("cwe_number", out var numberElement)) { if (numberElement.ValueKind == JsonValueKind.Number && numberElement.TryGetInt32(out var parsed)) { number = parsed; } else if (int.TryParse(numberElement.GetString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedInt)) { number = parsedInt; } } var cweDescription = Normalize(cweElement.TryGetProperty("cwe_description", out var descElement) ? descElement.GetString() : null); if (number.HasValue || !string.IsNullOrWhiteSpace(cweDescription)) { cweDto = new RuNkckiCweDto(number, cweDescription); } } double? cvssScore = element.TryGetProperty("cvss", out var cvssElement) && cvssElement.TryGetProperty("cvss_score", out var scoreElement) ? ParseDouble(scoreElement) : null; var cvssVector = element.TryGetProperty("cvss", out cvssElement) && cvssElement.TryGetProperty("cvss_vector", out var vectorElement) ? Normalize(vectorElement.GetString()) : null; double? cvssScoreV4 = element.TryGetProperty("cvss", out cvssElement) && cvssElement.TryGetProperty("cvss_score_v4", out var scoreV4Element) ? ParseDouble(scoreV4Element) : null; var cvssVectorV4 = element.TryGetProperty("cvss", out cvssElement) && cvssElement.TryGetProperty("cvss_vector_v4", out var vectorV4Element) ? Normalize(vectorV4Element.GetString()) : null; var urls = element.TryGetProperty("urls", out var urlsElement) && urlsElement.ValueKind == JsonValueKind.Array ? urlsElement.EnumerateArray() .Select(static url => Normalize(url.GetString())) .Where(static url => !string.IsNullOrWhiteSpace(url)) .Cast() .ToImmutableArray() : ImmutableArray.Empty; return new RuNkckiVulnerabilityDto( fstecId, mitreId, datePublished, dateUpdated, cvssRating, patchAvailable, description, cweDto, productCategory, mitigation, softwareText, softwareHasCpe, cvssScore, cvssVector, cvssScoreV4, cvssVectorV4, impact, method, userInteraction, urls); } private static double? ParseDouble(JsonElement element) { if (element.ValueKind == JsonValueKind.Number && element.TryGetDouble(out var value)) { return value; } if (element.ValueKind == JsonValueKind.String && double.TryParse(element.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var parsed)) { return parsed; } return null; } private static DateTimeOffset? ParseDate(string? value) { if (string.IsNullOrWhiteSpace(value)) { return null; } if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var parsed)) { return parsed; } if (DateTimeOffset.TryParse(value, CultureInfo.GetCultureInfo("ru-RU"), DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ruParsed)) { return ruParsed; } return null; } private static string? Normalize(string? value) { if (string.IsNullOrWhiteSpace(value)) { return null; } return value.Replace('\r', ' ').Replace('\n', ' ').Trim(); } }