public Info(OSSVulnerability v, FeedType feedType, string packageName, IEnumerable <string> versions) { this.v = v; this.FeedType = feedType; this.PackageName = packageName; this.PackageVersions = VulnerabilityPackageVersionRange.Multiple(versions.Select(VulnerabilityPackageVersionRange.Single)); }
public override async IAsyncEnumerable <VulnerabilityInfo> GetVulnerabilitiesAsync(IVulnerabilitySourceContext context, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (context == null) { throw new ArgumentNullException(nameof(context)); } using (var client = this.GetHttpClient()) { var serializer = new JsonSerializer(); var receivedVulnerabilities = new Dictionary <string, OSSVulnerability>(); foreach (var chunk in GetCoordinateChunks(context.Packages)) { int retries = 0; Retry: using (var content = getChunkContent(chunk)) using (var response = await client.PostAsync("https://ossindex.sonatype.org/api/v3/component-report", content, cancellationToken)) { if ((int)response.StatusCode == 429 && retries < 5) { this.LogWarning($"Detected rate limiting. Waiting five minutes before retrying. Attempt {(retries + 1)} of 5."); await Task.Delay(TimeSpan.FromMinutes(5), cancellationToken); retries++; goto Retry; } if (!response.IsSuccessStatusCode) { this.LogError("OSS Index returned failure error code: " + (int)response.StatusCode); this.LogDebug("Content of response: " + await response.Content.ReadAsStringAsync(cancellationToken)); yield break; } using (var responseStream = new BufferedStream(await response.Content.ReadAsStreamAsync(cancellationToken))) using (var textReader = new StreamReader(responseStream, InedoLib.UTF8Encoding)) using (var jsonTextReader = new JsonTextReader(textReader)) { var pvs = serializer.Deserialize <IEnumerable <PackageVulnerabilities> >(jsonTextReader); this.LogDebug($"Request returned {pvs.Count()} vulnerability records."); foreach (var pv in pvs) { var match = CoordinateRegex.Match(pv.Coordinates); if (!match.Success) { this.LogWarning($"Unable to parse returned package coordinate string '{pv.Coordinates}'"); continue; } var feedType = ParseFeedType(match.Groups[1].Value); if (feedType == null) { this.LogWarning($"Unable to determine feed type for returned package coordinate string '{pv.Coordinates}'"); continue; } var packageName = Uri.UnescapeDataString(match.Groups[2].Value); var packageVersion = match.Groups[3].Value; foreach (var v in pv.Vulnerabilities) { if (!receivedVulnerabilities.TryGetValue(v.Id, out var vulnerability)) { vulnerability = new OSSVulnerability(v.Id, v.Title, v.Description, v.CVSSScore, v.Reference); receivedVulnerabilities.Add(v.Id, vulnerability); } vulnerability.AddPackageVersion(feedType.Value, packageName, packageVersion); } } } } } foreach (var vulnInfo in receivedVulnerabilities.Values.SelectMany(v => v.GetVulnerabilityInfos())) { yield return(vulnInfo); } } HttpContent getChunkContent(string[] chunk) { var stream = new MemoryStream(); using (var textWriter = new StreamWriter(stream, InedoLib.UTF8Encoding, 16, true)) using (var jsonWriter = new JsonTextWriter(textWriter)) { jsonWriter.WriteStartObject(); jsonWriter.WritePropertyName("coordinates"); jsonWriter.WriteStartArray(); foreach (var c in chunk) { jsonWriter.WriteValue(c); } jsonWriter.WriteEndArray(); jsonWriter.WriteEndObject(); } stream.Position = 0; return(new StreamContent(stream) { Headers = { ContentType = new MediaTypeHeaderValue("application/vnd.ossindex.component-report-request.v1+json") } }); } }