public override async Task <IEnumerable <VulnerabilityInfo> > GetVulnerabilitiesAsync(IVulnerabilitySourceContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var packages = context.Packages.Select(PackageId.TryCreate).Where(p => p != null).ToArray(); if (!packages.Any()) { return(Enumerable.Empty <VulnerabilityInfo>()); } using (var client = new HttpClient()) using (var content = new StringContent(JsonConvert.SerializeObject(packages), InedoLib.UTF8Encoding, "application/json")) using (var response = await client.PostAsync("https://ossindex.net/v2.0/package", content)) { response.EnsureSuccessStatusCode(); return(JsonConvert.DeserializeObject <IEnumerable <PackageVulnerabilities> >(await response.Content.ReadAsStringAsync()) .SelectMany(p => p.Vulnerabilities.Select(v => v.GetInfo(p, packages)))); } }
public override async Task <IEnumerable <VulnerabilityInfo> > GetVulnerabilitiesAsync(IVulnerabilitySourceContext context) { var serializer = new JsonSerializer(); var layersApi = ApiUrl.Trim('/') + "/v1/layers"; var vulnerabilitesApiUrl = ApiUrl.Trim('/') + "/v1/layers/{0}?features&vulnerabilities"; var vulnerabilityInfos = new List <VulnerabilityInfo>(); foreach (var blob in context.Blobs.Where(b => !b.MediaType.EndsWith("json", StringComparison.OrdinalIgnoreCase))) { var postRequest = this.CreatePostWebRequest(layersApi); var response = await this.PushLayerToClair(serializer, postRequest, blob); if (response != null) { var vResponse = await this.GetVulnerabilitesFromClair(serializer, vulnerabilitesApiUrl, blob, response); if (vResponse?.Layer?.Features?.Any(f => f?.Vulnerabilities?.Any(v => v != null) ?? false) != null) { var vulnerableFeatures = vResponse.Layer.Features.Where(f => f.Vulnerabilities?.Any() ?? false); foreach (var vulnerableFeature in vulnerableFeatures) { foreach (var vulnerability in vulnerableFeature.Vulnerabilities) { vulnerabilityInfos.Add(new ClairVulnerabilityInfo(this.BuildId(vulnerableFeature), blob.FeedType, blob.Digest, vulnerability.Name, this.BuildDescription(vulnerableFeature, vulnerability))); } } } } } return(vulnerabilityInfos); }
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") } }); } }