170 lines
6.8 KiB
C#
170 lines
6.8 KiB
C#
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<string>()
|
|
.ToImmutableArray()
|
|
: ImmutableArray<string>.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();
|
|
}
|
|
}
|