From 9be04ab2c604b65b9cf0ebea98b699a1bdbc4b40 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh <117642191+georgii-borovinskikh-sonarsource@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:02:50 +0100 Subject: [PATCH] SLVS-1628 Remove taint related methods from SonarQubeService (#5833) [SLVS-1628](https://sonarsource.atlassian.net/browse/SLVS-1628) [SLVS-1628]: https://sonarsource.atlassian.net/browse/SLVS-1628?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --- .../Api/V7_20/GetIssuesRequestTests.cs | 6 + .../Api/V7_20/GetIssuesRequestWrapperTests.cs | 207 +- .../DefaultConfiguration_Configure_Tests.cs | 345 ++- ...narQubeService_GetSuppressedIssuesAsync.cs | 429 ++-- ...eService_GetTaintVulnerabilitiesRequest.cs | 1963 ----------------- .../Api/DefaultConfiguration.cs | 147 +- src/SonarQube.Client/Api/IGetIssuesRequest.cs | 31 +- .../Api/IGetTaintVulnerabilitiesRequest.cs | 39 - .../GetTaintVulnerabilitiesWithCCTRequest.cs | 38 - .../Api/V5_10/GetIssuesRequest.cs | 122 +- .../Api/V7_20/GetIssuesRequest.cs | 237 +- .../Api/V7_20/GetIssuesRequestWrapper.cs | 148 +- .../V8_6/GetTaintVulnerabilitiesRequest.cs | 65 - ...tTaintVulnerabilitiesWithContextRequest.cs | 38 - src/SonarQube.Client/ISonarQubeService.cs | 8 - src/SonarQube.Client/SonarQubeService.cs | 14 - 16 files changed, 757 insertions(+), 3080 deletions(-) delete mode 100644 src/SonarQube.Client.Tests/SonarQubeService_GetTaintVulnerabilitiesRequest.cs delete mode 100644 src/SonarQube.Client/Api/IGetTaintVulnerabilitiesRequest.cs delete mode 100644 src/SonarQube.Client/Api/V10_2/GetTaintVulnerabilitiesWithCCTRequest.cs delete mode 100644 src/SonarQube.Client/Api/V8_6/GetTaintVulnerabilitiesRequest.cs delete mode 100644 src/SonarQube.Client/Api/V9_6/GetTaintVulnerabilitiesWithContextRequest.cs diff --git a/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestTests.cs b/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestTests.cs index b0203f0141..3d6c8724b9 100644 --- a/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestTests.cs +++ b/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestTests.cs @@ -50,6 +50,9 @@ public async Task InvokeAsync_FilePathNormalized() BaseAddress = new Uri(ValidBaseAddress) }; + // the contents of the json below were left untouched during the cleanup of the taint related methods & requests of SonarQube.Client + // because they still represent a valid issue format (with flows and secondary locations), + // while the fact that the contents have mentions of taint in them is irrelevant for this test var request = $"api/issues/search?projects={projectKey}&statuses={expectedEscapedStatusesInRequest}&p=1&ps=500"; const string response = @" { @@ -138,6 +141,9 @@ public async Task InvokeAsync_ResponseWithFlows_IsDeserializedCorrectly() BaseAddress = new Uri(ValidBaseAddress) }; + // the contents of the json below were left untouched during the cleanup of the taint related methods & requests of SonarQube.Client + // because they still represent a valid issue format (with flows and secondary locations), + // while the fact that the contents have mentions of taint in them is irrelevant for this test var request = $"api/issues/search?projects={projectKey}&statuses={expectedEscapedStatusesInRequest}&p=1&ps=500"; const string response = @" { diff --git a/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestWrapperTests.cs b/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestWrapperTests.cs index fb3465b55f..963b2cd670 100644 --- a/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestWrapperTests.cs +++ b/src/SonarQube.Client.Tests/Requests/Api/V7_20/GetIssuesRequestWrapperTests.cs @@ -18,142 +18,135 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SonarQube.Client.Api; using SonarQube.Client.Api.V7_20; using SonarQube.Client.Tests.Infra; using static SonarQube.Client.Tests.Infra.MocksHelper; -namespace SonarQube.Client.Tests.Requests.Api.V7_20 +namespace SonarQube.Client.Tests.Requests.Api.V7_20; + +[TestClass] +public class GetIssuesRequestWrapperTests { - [TestClass] - public class GetIssuesRequestWrapperTests + private const string ComponentPropertyNameSonarQube = "components"; + private const string ComponentPropertyNameSonarCloud = "componentKeys"; + + [DataTestMethod] + [DataRow(ComponentPropertyNameSonarQube, DisplayName = "SonarQube")] + [DataRow(ComponentPropertyNameSonarCloud, DisplayName = "SonarCloud")] + public async Task InvokeAsync_NoIssueKeys_ExpectedPropertiesArePassedInMultipleRequests(string componentPropertyName) { - private const string ComponentPropertyNameSonarQube = "components"; - private const string ComponentPropertyNameSonarCloud = "componentKeys"; - - [DataTestMethod] - [DataRow(ComponentPropertyNameSonarQube, DisplayName = "SonarQube")] - [DataRow(ComponentPropertyNameSonarCloud, DisplayName = "SonarCloud")] - public async Task InvokeAsync_NoIssueKeys_ExpectedPropertiesArePassedInMultipleRequests(string componentPropertyName) + var testSubject = CreateTestSubject(componentPropertyName, "aaaProject", "xStatus", "yBranch", null, "rule1", "component1"); + + var handlerMock = new Mock(MockBehavior.Strict); + var httpClient = new HttpClient(handlerMock.Object) { - var testSubject = CreateTestSubject(componentPropertyName, "aaaProject", "xStatus", "yBranch", null, "rule1", "component1"); + BaseAddress = new Uri(ValidBaseAddress) + }; - var handlerMock = new Mock(MockBehavior.Strict); - var httpClient = new HttpClient(handlerMock.Object) - { - BaseAddress = new Uri(ValidBaseAddress) - }; + SetupHttpRequest(handlerMock, EmptyGetIssuesResponse); - SetupHttpRequest(handlerMock, EmptyGetIssuesResponse); + _ = await testSubject.InvokeAsync(httpClient, CancellationToken.None); - _ = await testSubject.InvokeAsync(httpClient, CancellationToken.None); + // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities + handlerMock.Invocations.Count.Should().Be(2); + CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 0, expectedTypes: "CODE_SMELL"); + CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 1, expectedTypes: "BUG"); + } - // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities - handlerMock.Invocations.Count.Should().Be(3); - CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 0, expectedTypes: "CODE_SMELL"); - CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 1, expectedTypes: "BUG"); - CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 2, expectedTypes: "VULNERABILITY"); - } + [DataTestMethod] + [DataRow(ComponentPropertyNameSonarQube, DisplayName = "SonarQube")] + [DataRow(ComponentPropertyNameSonarCloud, DisplayName = "SonarCloud")] + public async Task InvokeAsync_HasIssueKeys_ExpectedPropertiesArePassedInASingleRequest(string componentPropertyName) + { + var issueKeys = new[] { "issue1", "issue2" }; + var testSubject = CreateTestSubject(componentPropertyName,"aaaProject", "xStatus", "yBranch", issueKeys, "rule1", "component1"); - [DataTestMethod] - [DataRow(ComponentPropertyNameSonarQube, DisplayName = "SonarQube")] - [DataRow(ComponentPropertyNameSonarCloud, DisplayName = "SonarCloud")] - public async Task InvokeAsync_HasIssueKeys_ExpectedPropertiesArePassedInASingleRequest(string componentPropertyName) + var handlerMock = new Mock(MockBehavior.Strict); + var httpClient = new HttpClient(handlerMock.Object) { - var issueKeys = new[] { "issue1", "issue2" }; - var testSubject = CreateTestSubject(componentPropertyName,"aaaProject", "xStatus", "yBranch", issueKeys, "rule1", "component1"); + BaseAddress = new Uri(ValidBaseAddress) + }; - var handlerMock = new Mock(MockBehavior.Strict); - var httpClient = new HttpClient(handlerMock.Object) - { - BaseAddress = new Uri(ValidBaseAddress) - }; + SetupHttpRequest(handlerMock, EmptyGetIssuesResponse); - SetupHttpRequest(handlerMock, EmptyGetIssuesResponse); + _ = await testSubject.InvokeAsync(httpClient, CancellationToken.None); - _ = await testSubject.InvokeAsync(httpClient, CancellationToken.None); + // The wrapper is expected to make one call with the given issueKeys + handlerMock.Invocations.Count.Should().Be(1); - // The wrapper is expected to make one call with the given issueKeys - handlerMock.Invocations.Count.Should().Be(1); - - CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 0, expectedKeys: issueKeys); - } - - private static IGetIssuesRequest CreateTestSubject(string componentPropertyName, string projectKey, string statusesToRequest, string branch, string[] issueKeys, string ruleId, string componentKey) - { - return componentPropertyName switch - { - ComponentPropertyNameSonarQube => new GetIssuesRequestWrapper - { - Logger = new TestLogger(), - ProjectKey = projectKey, - Statuses = statusesToRequest, - Branch = branch, - IssueKeys = issueKeys, - RuleId = ruleId, - ComponentKey = componentKey - }, - ComponentPropertyNameSonarCloud => new GetIssuesRequestWrapper - { - Logger = new TestLogger(), - ProjectKey = projectKey, - Statuses = statusesToRequest, - Branch = branch, - IssueKeys = issueKeys, - RuleId = ruleId, - ComponentKey = componentKey - }, - _ => throw new ArgumentOutOfRangeException() - }; - } + CheckExpectedQueryStringsParameters(componentPropertyName, handlerMock, 0, expectedKeys: issueKeys); + } - private static void CheckExpectedQueryStringsParameters(string componentKeyName, - Mock handlerMock, - int invocationIndex, - string expectedTypes = null, - string[] expectedKeys = null) + private static IGetIssuesRequest CreateTestSubject(string componentPropertyName, string projectKey, string statusesToRequest, string branch, string[] issueKeys, string ruleId, string componentKey) + { + return componentPropertyName switch { - var actualQueryString = GetActualQueryStringForInvocation(handlerMock, invocationIndex); - - Console.WriteLine($"Invocation [{invocationIndex}]: {actualQueryString}"); - actualQueryString.Contains($"?{componentKeyName}=component1").Should().BeTrue(); - actualQueryString.Contains("&projects=aaaProject").Should().BeTrue(); - actualQueryString.Contains("&statuses=xStatus").Should().BeTrue(); - actualQueryString.Contains("&branch=yBranch").Should().BeTrue(); - actualQueryString.Contains("&rules=rule1").Should().BeTrue(); - - if (expectedTypes != null) + ComponentPropertyNameSonarQube => new GetIssuesRequestWrapper { - actualQueryString.Contains($"&types={expectedTypes}").Should().BeTrue(); - } - else + Logger = new TestLogger(), + ProjectKey = projectKey, + Statuses = statusesToRequest, + Branch = branch, + IssueKeys = issueKeys, + RuleId = ruleId, + ComponentKey = componentKey + }, + ComponentPropertyNameSonarCloud => new GetIssuesRequestWrapper { - actualQueryString.Contains("types").Should().BeFalse(); - } + Logger = new TestLogger(), + ProjectKey = projectKey, + Statuses = statusesToRequest, + Branch = branch, + IssueKeys = issueKeys, + RuleId = ruleId, + ComponentKey = componentKey + }, + _ => throw new ArgumentOutOfRangeException() + }; + } - if (expectedKeys != null) - { - var keys = string.Join("%2C", expectedKeys); - actualQueryString.Contains($"&issues={keys}").Should().BeTrue(); - } - else - { - actualQueryString.Contains("issues").Should().BeFalse(); - } + private static void CheckExpectedQueryStringsParameters(string componentKeyName, + Mock handlerMock, + int invocationIndex, + string expectedTypes = null, + string[] expectedKeys = null) + { + var actualQueryString = GetActualQueryStringForInvocation(handlerMock, invocationIndex); + + Console.WriteLine($"Invocation [{invocationIndex}]: {actualQueryString}"); + actualQueryString.Contains($"?{componentKeyName}=component1").Should().BeTrue(); + actualQueryString.Contains("&projects=aaaProject").Should().BeTrue(); + actualQueryString.Contains("&statuses=xStatus").Should().BeTrue(); + actualQueryString.Contains("&branch=yBranch").Should().BeTrue(); + actualQueryString.Contains("&rules=rule1").Should().BeTrue(); + if (expectedTypes != null) + { + actualQueryString.Contains($"&types={expectedTypes}").Should().BeTrue(); + } + else + { + actualQueryString.Contains("types").Should().BeFalse(); } - private static string GetActualQueryStringForInvocation(Mock handlerMock, int invocationIndex) + if (expectedKeys != null) + { + var keys = string.Join("%2C", expectedKeys); + actualQueryString.Contains($"&issues={keys}").Should().BeTrue(); + } + else { - var requestMessage = (HttpRequestMessage)handlerMock.Invocations[invocationIndex].Arguments[0]; - return requestMessage.RequestUri.Query; + actualQueryString.Contains("issues").Should().BeFalse(); } + + } + + private static string GetActualQueryStringForInvocation(Mock handlerMock, int invocationIndex) + { + var requestMessage = (HttpRequestMessage)handlerMock.Invocations[invocationIndex].Arguments[0]; + return requestMessage.RequestUri.Query; } } diff --git a/src/SonarQube.Client.Tests/Requests/DefaultConfiguration_Configure_Tests.cs b/src/SonarQube.Client.Tests/Requests/DefaultConfiguration_Configure_Tests.cs index 2405cbe246..59ade660a9 100644 --- a/src/SonarQube.Client.Tests/Requests/DefaultConfiguration_Configure_Tests.cs +++ b/src/SonarQube.Client.Tests/Requests/DefaultConfiguration_Configure_Tests.cs @@ -18,199 +18,188 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Diagnostics; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using SonarQube.Client.Api; using SonarQube.Client.Requests; using SonarQube.Client.Tests.Infra; -namespace SonarQube.Client.Tests.Requests +namespace SonarQube.Client.Tests.Requests; + +[TestClass] +public class DefaultConfiguration_Configure_Tests { - [TestClass] - public class DefaultConfiguration_Configure_Tests + [TestMethod] + public void ConfigureSonarQube_Writes_Debug_Messages() { - [TestMethod] - public void ConfigureSonarQube_Writes_Debug_Messages() - { - var logger = new TestLogger(); - - var expected = new[] - { - "Registered SonarQube.Client.Api.V2_10.GetPluginsRequest for 2.1", - "Registered SonarQube.Client.Api.V2_10.GetProjectsRequest for 2.1", - "Registered SonarQube.Client.Api.V2_10.GetVersionRequest for 2.1", - "Registered SonarQube.Client.Api.V2_60.GetPropertiesRequest for 2.6", - "Registered SonarQube.Client.Api.V3_30.ValidateCredentialsRequest for 3.3", - "Registered SonarQube.Client.Api.V5_00.GetSourceCodeRequest for 5.0", - "Registered SonarQube.Client.Api.V5_10.GetIssuesRequest for 5.1", - "Registered SonarQube.Client.Api.V5_10.GetLanguagesRequest for 5.1", - "Registered SonarQube.Client.Api.V5_20.GetQualityProfileChangeLogRequest for 5.2", - "Registered SonarQube.Client.Api.V5_20.GetQualityProfilesRequest for 5.2", - "Registered SonarQube.Client.Api.V5_20.GetRoslynExportProfileRequest for 5.2", - "Registered SonarQube.Client.Api.V5_40.GetModulesRequest for 5.4", - "Registered SonarQube.Client.Api.V5_50.GetRulesRequest for 5.5", - "Registered SonarQube.Client.Api.V5_50.DownloadStaticFile for 5.5", - "Registered SonarQube.Client.Api.V6_20.GetOrganizationsRequest for 6.2", - "Registered SonarQube.Client.Api.V6_20.GetProjectsRequest for 6.2", - "Registered SonarQube.Client.Api.V6_30.GetPluginsRequest for 6.3", - "Registered SonarQube.Client.Api.V6_30.GetPropertiesRequest for 6.3", - "Registered SonarQube.Client.Api.V6_50.GetQualityProfileChangeLogRequest for 6.5", - "Registered SonarQube.Client.Api.V6_50.GetQualityProfilesRequest for 6.5", - "Registered SonarQube.Client.Api.V6_60.GetNotificationsRequest for 6.6", - "Registered SonarQube.Client.Api.V6_60.GetRoslynExportProfileRequest for 6.6", - "Registered SonarQube.Client.Api.V6_60.GetProjectBranchesRequest for 6.6", - "Registered SonarQube.Client.Api.V7_00.GetOrganizationsRequest for 7.0", - "Registered SonarQube.Client.Api.V7_20.GetIssuesRequestWrapper`1[SonarQube.Client.Api.V7_20.GetIssuesWithComponentSonarQubeRequest] for 7.2", - "Registered SonarQube.Client.Api.V8_6.GetHotspotRequest for 8.6", - "Registered SonarQube.Client.Api.V8_6.GetTaintVulnerabilitiesRequest for 8.6", - "Registered SonarQube.Client.Api.V7_20.GetExclusionsRequest for 7.2", - "Registered SonarQube.Client.Api.V9_4.GetSonarLintEventStream for 9.4", - "Registered SonarQube.Client.Api.V9_5.GetRulesWithDescriptionSectionsRequest for 9.5", - "Registered SonarQube.Client.Api.V9_6.GetRulesWithEducationPrinciplesRequest for 9.6", - "Registered SonarQube.Client.Api.V9_6.GetTaintVulnerabilitiesWithContextRequest for 9.6", - "Registered SonarQube.Client.Api.V9_7.SearchHotspotRequest for 9.7", - "Registered SonarQube.Client.Api.V10_2.SearchHotspotRequest for 10.2", - "Registered SonarQube.Client.Api.V10_2.GetTaintVulnerabilitiesWithCCTRequest for 10.2", - "Registered SonarQube.Client.Api.V10_2.GetRulesWithCCTRequest for 10.2", - "Registered SonarQube.Client.Api.V9_9.TransitionIssueRequestWithWontFix for 9.9", - "Registered SonarQube.Client.Api.V10_4.TransitionIssueRequestWithAccept for 10.4", - "Registered SonarQube.Client.Api.V9_9.CommentIssueRequest for 9.9", - "Registered SonarQube.Client.Api.V9_9.SearchFilesByNameRequest for 9.9" - }; - - DefaultConfiguration.ConfigureSonarQube(new RequestFactory(logger)); - - DumpDebugMessages(logger); - - logger.DebugMessages.Should().ContainInOrder(expected); - logger.DebugMessages.Count.Should().Be(expected.Length); - } + var logger = new TestLogger(); - [TestMethod] - public void ConfigureSonarCloud_Writes_Debug_Messages() + var expected = new[] { - var logger = new TestLogger(); - - var expected = new string[] - { - "Registered SonarQube.Client.Api.V2_10.GetVersionRequest", - "Registered SonarQube.Client.Api.V3_30.ValidateCredentialsRequest", - "Registered SonarQube.Client.Api.V5_00.GetSourceCodeRequest", - "Registered SonarQube.Client.Api.V5_10.GetLanguagesRequest", - "Registered SonarQube.Client.Api.V5_40.GetModulesRequest", - "Registered SonarQube.Client.Api.V10_2.GetRulesWithCCTRequest", - "Registered SonarQube.Client.Api.V5_50.DownloadStaticFile", - "Registered SonarQube.Client.Api.V6_20.GetProjectsRequest", - "Registered SonarQube.Client.Api.V6_30.GetPluginsRequest", - "Registered SonarQube.Client.Api.V6_30.GetPropertiesRequest", - "Registered SonarQube.Client.Api.V6_50.GetQualityProfileChangeLogRequest", - "Registered SonarQube.Client.Api.V6_50.GetQualityProfilesRequest", - "Registered SonarQube.Client.Api.V6_60.GetNotificationsRequest", - "Registered SonarQube.Client.Api.V6_60.GetRoslynExportProfileRequest", - "Registered SonarQube.Client.Api.V6_60.GetProjectBranchesRequest", - "Registered SonarQube.Client.Api.V7_00.GetOrganizationsRequest", - "Registered SonarQube.Client.Api.V7_20.GetIssuesRequestWrapper`1[SonarQube.Client.Api.V7_20.GetIssuesWithComponentSonarCloudRequest]", - "Registered SonarQube.Client.Api.V8_6.GetHotspotRequest", - "Registered SonarQube.Client.Api.V10_2.GetTaintVulnerabilitiesWithCCTRequest", - "Registered SonarQube.Client.Api.V7_20.GetExclusionsRequest", - "Registered SonarQube.Client.Api.V9_7.SearchHotspotRequest", - "Registered SonarQube.Client.Api.V10_4.TransitionIssueRequestWithAccept", - "Registered SonarQube.Client.Api.V9_9.CommentIssueRequest", - "Registered SonarQube.Client.Api.V9_9.SearchFilesByNameRequest" - }; - - DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(logger)); - - DumpDebugMessages(logger); - - logger.DebugMessages.Should().ContainInOrder(expected); - logger.DebugMessages.Count.Should().Be(expected.Length); - } + "Registered SonarQube.Client.Api.V2_10.GetPluginsRequest for 2.1", + "Registered SonarQube.Client.Api.V2_10.GetProjectsRequest for 2.1", + "Registered SonarQube.Client.Api.V2_10.GetVersionRequest for 2.1", + "Registered SonarQube.Client.Api.V2_60.GetPropertiesRequest for 2.6", + "Registered SonarQube.Client.Api.V3_30.ValidateCredentialsRequest for 3.3", + "Registered SonarQube.Client.Api.V5_00.GetSourceCodeRequest for 5.0", + "Registered SonarQube.Client.Api.V5_10.GetIssuesRequest for 5.1", + "Registered SonarQube.Client.Api.V5_10.GetLanguagesRequest for 5.1", + "Registered SonarQube.Client.Api.V5_20.GetQualityProfileChangeLogRequest for 5.2", + "Registered SonarQube.Client.Api.V5_20.GetQualityProfilesRequest for 5.2", + "Registered SonarQube.Client.Api.V5_20.GetRoslynExportProfileRequest for 5.2", + "Registered SonarQube.Client.Api.V5_40.GetModulesRequest for 5.4", + "Registered SonarQube.Client.Api.V5_50.GetRulesRequest for 5.5", + "Registered SonarQube.Client.Api.V5_50.DownloadStaticFile for 5.5", + "Registered SonarQube.Client.Api.V6_20.GetOrganizationsRequest for 6.2", + "Registered SonarQube.Client.Api.V6_20.GetProjectsRequest for 6.2", + "Registered SonarQube.Client.Api.V6_30.GetPluginsRequest for 6.3", + "Registered SonarQube.Client.Api.V6_30.GetPropertiesRequest for 6.3", + "Registered SonarQube.Client.Api.V6_50.GetQualityProfileChangeLogRequest for 6.5", + "Registered SonarQube.Client.Api.V6_50.GetQualityProfilesRequest for 6.5", + "Registered SonarQube.Client.Api.V6_60.GetNotificationsRequest for 6.6", + "Registered SonarQube.Client.Api.V6_60.GetRoslynExportProfileRequest for 6.6", + "Registered SonarQube.Client.Api.V6_60.GetProjectBranchesRequest for 6.6", + "Registered SonarQube.Client.Api.V7_00.GetOrganizationsRequest for 7.0", + "Registered SonarQube.Client.Api.V7_20.GetIssuesRequestWrapper`1[SonarQube.Client.Api.V7_20.GetIssuesWithComponentSonarQubeRequest] for 7.2", + "Registered SonarQube.Client.Api.V8_6.GetHotspotRequest for 8.6", + "Registered SonarQube.Client.Api.V7_20.GetExclusionsRequest for 7.2", + "Registered SonarQube.Client.Api.V9_4.GetSonarLintEventStream for 9.4", + "Registered SonarQube.Client.Api.V9_5.GetRulesWithDescriptionSectionsRequest for 9.5", + "Registered SonarQube.Client.Api.V9_6.GetRulesWithEducationPrinciplesRequest for 9.6", + "Registered SonarQube.Client.Api.V9_7.SearchHotspotRequest for 9.7", + "Registered SonarQube.Client.Api.V10_2.SearchHotspotRequest for 10.2", + "Registered SonarQube.Client.Api.V10_2.GetRulesWithCCTRequest for 10.2", + "Registered SonarQube.Client.Api.V9_9.TransitionIssueRequestWithWontFix for 9.9", + "Registered SonarQube.Client.Api.V10_4.TransitionIssueRequestWithAccept for 10.4", + "Registered SonarQube.Client.Api.V9_9.CommentIssueRequest for 9.9", + "Registered SonarQube.Client.Api.V9_9.SearchFilesByNameRequest for 9.9" + }; + + DefaultConfiguration.ConfigureSonarQube(new RequestFactory(logger)); + + DumpDebugMessages(logger); + + logger.DebugMessages.Should().ContainInOrder(expected); + logger.DebugMessages.Count.Should().Be(expected.Length); + } - [TestMethod] - public void ConfigureSonarQube_CheckAllRequestsImplemented() - { - var testSubject = DefaultConfiguration.ConfigureSonarQube(new RequestFactory(new TestLogger())); - var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); - - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - } + [TestMethod] + public void ConfigureSonarCloud_Writes_Debug_Messages() + { + var logger = new TestLogger(); - [TestMethod] - public void ConfigureSonarCloud_CheckAllRequestsImplemented() + var expected = new string[] { - var testSubject = DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(new TestLogger())); - var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); - - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - testSubject.Create(serverInfo).Should().NotBeNull(); - } + "Registered SonarQube.Client.Api.V2_10.GetVersionRequest", + "Registered SonarQube.Client.Api.V3_30.ValidateCredentialsRequest", + "Registered SonarQube.Client.Api.V5_00.GetSourceCodeRequest", + "Registered SonarQube.Client.Api.V5_10.GetLanguagesRequest", + "Registered SonarQube.Client.Api.V5_40.GetModulesRequest", + "Registered SonarQube.Client.Api.V10_2.GetRulesWithCCTRequest", + "Registered SonarQube.Client.Api.V5_50.DownloadStaticFile", + "Registered SonarQube.Client.Api.V6_20.GetProjectsRequest", + "Registered SonarQube.Client.Api.V6_30.GetPluginsRequest", + "Registered SonarQube.Client.Api.V6_30.GetPropertiesRequest", + "Registered SonarQube.Client.Api.V6_50.GetQualityProfileChangeLogRequest", + "Registered SonarQube.Client.Api.V6_50.GetQualityProfilesRequest", + "Registered SonarQube.Client.Api.V6_60.GetNotificationsRequest", + "Registered SonarQube.Client.Api.V6_60.GetRoslynExportProfileRequest", + "Registered SonarQube.Client.Api.V6_60.GetProjectBranchesRequest", + "Registered SonarQube.Client.Api.V7_00.GetOrganizationsRequest", + "Registered SonarQube.Client.Api.V7_20.GetIssuesRequestWrapper`1[SonarQube.Client.Api.V7_20.GetIssuesWithComponentSonarCloudRequest]", + "Registered SonarQube.Client.Api.V8_6.GetHotspotRequest", + "Registered SonarQube.Client.Api.V7_20.GetExclusionsRequest", + "Registered SonarQube.Client.Api.V9_7.SearchHotspotRequest", + "Registered SonarQube.Client.Api.V10_4.TransitionIssueRequestWithAccept", + "Registered SonarQube.Client.Api.V9_9.CommentIssueRequest", + "Registered SonarQube.Client.Api.V9_9.SearchFilesByNameRequest" + }; + + DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(logger)); + + DumpDebugMessages(logger); + + logger.DebugMessages.Should().ContainInOrder(expected); + logger.DebugMessages.Count.Should().Be(expected.Length); + } - [TestMethod] - [Description("The following APIs are not implemented on SC (yet). Verify that they are not registered in the factory.")] - public void ConfigureSonarCloud_CheckUnsupportedRequestsAreNotImplemented() - { - var testSubject = DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(new TestLogger())); - var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); + [TestMethod] + public void ConfigureSonarQube_CheckAllRequestsImplemented() + { + var testSubject = DefaultConfiguration.ConfigureSonarQube(new RequestFactory(new TestLogger())); + var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); + + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + } - Action act = () => testSubject.Create(serverInfo); - act.Should().Throw().And.Message.Should() - .Be("Could not find factory for 'IGetSonarLintEventStream'."); - } + [TestMethod] + public void ConfigureSonarCloud_CheckAllRequestsImplemented() + { + var testSubject = DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(new TestLogger())); + var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); + + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + testSubject.Create(serverInfo).Should().NotBeNull(); + } - private static void DumpDebugMessages(TestLogger logger) + [TestMethod] + [Description("The following APIs are not implemented on SC (yet). Verify that they are not registered in the factory.")] + public void ConfigureSonarCloud_CheckUnsupportedRequestsAreNotImplemented() + { + var testSubject = DefaultConfiguration.ConfigureSonarCloud(new UnversionedRequestFactory(new TestLogger())); + var serverInfo = new ServerInfo(null /* latest */, ServerType.SonarQube); + + Action act = () => testSubject.Create(serverInfo); + act.Should().Throw().And.Message.Should() + .Be("Could not find factory for 'IGetSonarLintEventStream'."); + } + + private static void DumpDebugMessages(TestLogger logger) + { + Debug.WriteLine("Actual registered requests:"); + foreach (var message in logger.DebugMessages) { - Debug.WriteLine("Actual registered requests:"); - foreach (var message in logger.DebugMessages) - { - Debug.WriteLine(message); - } + Debug.WriteLine(message); } } } diff --git a/src/SonarQube.Client.Tests/SonarQubeService_GetSuppressedIssuesAsync.cs b/src/SonarQube.Client.Tests/SonarQubeService_GetSuppressedIssuesAsync.cs index e5fc75e2d2..e0c0bb9fc0 100644 --- a/src/SonarQube.Client.Tests/SonarQubeService_GetSuppressedIssuesAsync.cs +++ b/src/SonarQube.Client.Tests/SonarQubeService_GetSuppressedIssuesAsync.cs @@ -18,66 +18,59 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SonarQube.Client.Models; using static SonarQube.Client.Tests.Infra.MocksHelper; -namespace SonarQube.Client.Tests +namespace SonarQube.Client.Tests; + +[TestClass] +public class SonarQubeService_GetSuppressedIssuesAsync : SonarQubeService_GetIssuesBase { - [TestClass] - public class SonarQubeService_GetSuppressedIssuesAsync : SonarQubeService_GetIssuesBase + [TestMethod] + public async Task GetSuppressedIssuesAsync_Old_ExampleFromSonarQube() { - [TestMethod] - public async Task GetSuppressedIssuesAsync_Old_ExampleFromSonarQube() - { - await ConnectToSonarQube(); + await ConnectToSonarQube(); - using (var reader = new StreamReader(@"TestResources\IssuesProtobufResponse")) - { - SetupRequest("batch/issues?key=project1", reader.ReadToEnd()); - } + using (var reader = new StreamReader(@"TestResources\IssuesProtobufResponse")) + { + SetupRequest("batch/issues?key=project1", reader.ReadToEnd()); + } - var result = await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); + var result = await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); - // TODO: create a protobuf file with more than one issue with different states - // the one above does not have suppressed issues, hence the Count==0 - result.Should().BeEmpty(); + // TODO: create a protobuf file with more than one issue with different states + // the one above does not have suppressed issues, hence the Count==0 + result.Should().BeEmpty(); - messageHandler.VerifyAll(); - } + messageHandler.VerifyAll(); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_Old_NotFound() - { - await ConnectToSonarQube(); + [TestMethod] + public async Task GetSuppressedIssuesAsync_Old_NotFound() + { + await ConnectToSonarQube(); - SetupRequest("batch/issues?key=project1", "", HttpStatusCode.NotFound); + SetupRequest("batch/issues?key=project1", "", HttpStatusCode.NotFound); - Func>> func = async () => - await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); + Func>> func = async () => + await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); - func.Should().ThrowExactly().And - .Message.Should().Be("Response status code does not indicate success: 404 (Not Found)."); + func.Should().ThrowExactly().And + .Message.Should().Be("Response status code does not indicate success: 404 (Not Found)."); - messageHandler.VerifyAll(); - } + messageHandler.VerifyAll(); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20() - { - await ConnectToSonarQube("7.2.0.0"); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20() + { + await ConnectToSonarQube("7.2.0.0"); - SetupRequest("api/issues/search?projects=shared&statuses=RESOLVED&types=CODE_SMELL&p=1&ps=500", @" + SetupRequest("api/issues/search?projects=shared&statuses=RESOLVED&types=CODE_SMELL&p=1&ps=500", @" { ""total"": 5, ""p"": 1, @@ -113,7 +106,7 @@ public async Task GetSuppressedIssuesAsync_From_7_20() ""components"": [ ] } "); - SetupRequest("api/issues/search?projects=shared&statuses=RESOLVED&types=BUG&p=1&ps=500", @" + SetupRequest("api/issues/search?projects=shared&statuses=RESOLVED&types=BUG&p=1&ps=500", @" { ""total"": 5, ""p"": 1, @@ -170,266 +163,166 @@ public async Task GetSuppressedIssuesAsync_From_7_20() } "); - SetupRequest("api/issues/search?projects=shared&statuses=RESOLVED&types=VULNERABILITY&p=1&ps=500", @" -{ - ""total"": 5, - ""p"": 1, - ""ps"": 100, - ""paging"": { - ""pageIndex"": 1, - ""pageSize"": 100, - ""total"": 5 - }, - ""issues"": [ - { - ""key"": ""AWg9DV27DpKqrfA7luen"", - ""rule"": ""csharpsquid:S1451"", - ""severity"": ""BLOCKER"", - ""component"": ""shared:SharedProject1/SharedClass1.cs"", - ""project"": ""shared"", - ""flows"": [], - ""resolution"": ""WONTFIX"", - ""status"": ""RESOLVED"", - ""message"": ""Add or update the header of this file."", - ""effort"": ""5min"", - ""debt"": ""5min"", - ""author"": """", - ""tags"": [], - ""creationDate"": ""2019-01-11T13:18:25+0100"", - ""updateDate"": ""2019-01-11T14:15:53+0100"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""fromHotspot"": false - }, - { - ""key"": ""AWg8adc9_JurIR2zdSvT"", - ""rule"": ""csharpsquid:S3400"", - ""severity"": ""MINOR"", - ""component"": ""shared:SharedProject1/SharedClass1.cs"", - ""project"": ""shared"", - ""line"": 5, - ""hash"": ""be411c6cf1ae5ba7d7c5d6da7355afa1"", - ""textRange"": { - ""startLine"": 5, - ""endLine"": 5, - ""startOffset"": 27, - ""endOffset"": 30 - }, - ""flows"": [], - ""resolution"": ""WONTFIX"", - ""status"": ""RESOLVED"", - ""message"": ""Remove this method and declare a constant for this value."", - ""effort"": ""5min"", - ""debt"": ""5min"", - ""author"": """", - ""tags"": [ - ""confusing"" - ], - ""creationDate"": ""2019-01-11T11:16:30+0100"", - ""updateDate"": ""2019-01-11T11:26:55+0100"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"" - } - ], - ""components"": [ - { - ""organization"": ""default-organization"", - ""key"": ""shared:SharedProject1/SharedClass1.cs"", - ""uuid"": ""AWg8adNl_JurIR2zdSvQ"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""SharedClass1.cs"", - ""longName"": ""SharedProject1/SharedClass1.cs"", - ""path"": ""SharedProject1/SharedClass1.cs"" + var result = await service.GetSuppressedIssuesAsync("shared", null, null, CancellationToken.None); + + result.Should().HaveCount(2); + + // Module level issues don't have FilePath, hash and line + result[0].FilePath.Should().Be(string.Empty); + result[0].Hash.Should().BeNull(); + result[0].TextRange.Should().BeNull(); + result[0].Message.Should().Be("Mark this assembly with 'System.CLSCompliantAttribute'"); + result[0].ModuleKey.Should().Be("shared:shared:2B470B7D-D47B-4E41-B105-D3938E196082"); + result[0].IsResolved.Should().BeTrue(); + result[0].RuleId.Should().Be("csharpsquid:S3990"); + result[0].Severity.Should().Be(SonarQubeIssueSeverity.Major); + + result[1].FilePath.Should().Be("Program.cs"); + result[1].Hash.Should().Be("0afa1b5e62aa3cfaf1cd9a4e63571cb5"); + result[1].TextRange.Should().BeEquivalentTo(new IssueTextRange(6, 6, 10, 17)); + result[1].Message.Should().Be("Add a 'protected' constructor or the 'static' keyword to the class declaration."); + result[1].ModuleKey.Should().Be("shared:shared:2B470B7D-D47B-4E41-B105-D3938E196082"); + result[1].IsResolved.Should().BeTrue(); + result[1].RuleId.Should().Be("csharpsquid:S1118"); + result[1].Severity.Should().Be(SonarQubeIssueSeverity.Major); + + messageHandler.VerifyAll(); } - ] -} -"); - - var result = await service.GetSuppressedIssuesAsync("shared", null, null, CancellationToken.None); - - result.Should().HaveCount(4); - - // Module level issues don't have FilePath, hash and line - result[0].FilePath.Should().Be(string.Empty); - result[0].Hash.Should().BeNull(); - result[0].TextRange.Should().BeNull(); - result[0].Message.Should().Be("Mark this assembly with 'System.CLSCompliantAttribute'"); - result[0].ModuleKey.Should().Be("shared:shared:2B470B7D-D47B-4E41-B105-D3938E196082"); - result[0].IsResolved.Should().BeTrue(); - result[0].RuleId.Should().Be("csharpsquid:S3990"); - result[0].Severity.Should().Be(SonarQubeIssueSeverity.Major); - - result[1].FilePath.Should().Be("Program.cs"); - result[1].Hash.Should().Be("0afa1b5e62aa3cfaf1cd9a4e63571cb5"); - result[1].TextRange.Should().BeEquivalentTo(new IssueTextRange(6, 6, 10, 17)); - result[1].Message.Should().Be("Add a 'protected' constructor or the 'static' keyword to the class declaration."); - result[1].ModuleKey.Should().Be("shared:shared:2B470B7D-D47B-4E41-B105-D3938E196082"); - result[1].IsResolved.Should().BeTrue(); - result[1].RuleId.Should().Be("csharpsquid:S1118"); - result[1].Severity.Should().Be(SonarQubeIssueSeverity.Major); - - // File level issues don't have hash and line - result[2].FilePath.Should().Be("SharedProject1\\SharedClass1.cs"); - result[2].Hash.Should().BeNull(); - result[2].TextRange.Should().BeNull(); - result[2].Message.Should().Be("Add or update the header of this file."); - result[2].ModuleKey.Should().Be("shared:SharedProject1/SharedClass1.cs"); - result[2].IsResolved.Should().BeTrue(); - result[2].RuleId.Should().Be("csharpsquid:S1451"); - result[2].Severity.Should().Be(SonarQubeIssueSeverity.Blocker); - - result[3].FilePath.Should().Be("SharedProject1\\SharedClass1.cs"); - result[3].Hash.Should().Be("be411c6cf1ae5ba7d7c5d6da7355afa1"); - result[3].TextRange.Should().BeEquivalentTo(new IssueTextRange(5, 5, 27, 30)); - result[3].Message.Should().Be("Remove this method and declare a constant for this value."); - result[3].ModuleKey.Should().Be("shared:SharedProject1/SharedClass1.cs"); - result[3].IsResolved.Should().BeTrue(); - result[3].RuleId.Should().Be("csharpsquid:S3400"); - result[3].Severity.Should().Be(SonarQubeIssueSeverity.Minor); - - messageHandler.VerifyAll(); - } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20_NotFound() - { - await ConnectToSonarQube("7.2.0.0"); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20_NotFound() + { + await ConnectToSonarQube("7.2.0.0"); - SetupRequest("api/issues/search?projects=project1&statuses=RESOLVED&types=CODE_SMELL&p=1&ps=500", "", HttpStatusCode.NotFound); + SetupRequest("api/issues/search?projects=project1&statuses=RESOLVED&types=CODE_SMELL&p=1&ps=500", "", HttpStatusCode.NotFound); - Func>> func = async () => - await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); + Func>> func = async () => + await service.GetSuppressedIssuesAsync("project1", null, null, CancellationToken.None); - func.Should().ThrowExactly().And - .Message.Should().Be("Response status code does not indicate success: 404 (Not Found)."); + func.Should().ThrowExactly().And + .Message.Should().Be("Response status code does not indicate success: 404 (Not Found)."); - messageHandler.VerifyAll(); - } + messageHandler.VerifyAll(); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20_Paging() - { - await ConnectToSonarQube("7.2.0.0"); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20_Paging() + { + await ConnectToSonarQube("7.2.0.0"); - SetupPagesOfResponses("simplcom", 1001, "CODE_SMELL"); - SetupPageOfResponses("simplcom", 1, 0, "BUG"); - SetupPageOfResponses("simplcom", 1, 0, "VULNERABILITY"); + SetupPagesOfResponses("simplcom", 1001, "CODE_SMELL"); + SetupPageOfResponses("simplcom", 1, 0, "BUG"); - var result = await service.GetSuppressedIssuesAsync("simplcom", null, null, CancellationToken.None); + var result = await service.GetSuppressedIssuesAsync("simplcom", null, null, CancellationToken.None); - result.Should().HaveCount(1001); - result.Select(i => i.FilePath).Should().Match(paths => paths.All(p => p == "Program.cs")); + result.Should().HaveCount(1001); + result.Select(i => i.FilePath).Should().Match(paths => paths.All(p => p == "Program.cs")); - messageHandler.VerifyAll(); - } + messageHandler.VerifyAll(); + } - [TestMethod] - // Note: we're not testing all possible combinations because testing with the - // max number of items is relatively slow (several seconds per iteration) - [DataRow(5, 5, 5)] // No issue types with too many issues - [DataRow(MaxAllowedIssues, 5, 2)] // One issue type with too many issues - [DataRow(1, MaxAllowedIssues, MaxAllowedIssues)] // Multiple issue types with too many issues - public async Task GetSuppressedIssuesAsync_From_7_20_NotifyWhenMaxIssuesReturned( - int numCodeSmells, int numBugs, int numVulnerabilities) - { - await ConnectToSonarQube("7.2.0.0"); + [TestMethod] + // Note: we're not testing all possible combinations because testing with the + // max number of items is relatively slow (several seconds per iteration) + [DataRow(5, 5)] // No issue types with too many issues + [DataRow(MaxAllowedIssues, 5)] // One issue type with too many issues + [DataRow(1, MaxAllowedIssues)] // Multiple issue types with too many issues + public async Task GetSuppressedIssuesAsync_From_7_20_NotifyWhenMaxIssuesReturned( + int numCodeSmells, int numBugs) + { + await ConnectToSonarQube("7.2.0.0"); - SetupPagesOfResponses("proj1", numCodeSmells, "CODE_SMELL"); - SetupPagesOfResponses("proj1", numBugs, "BUG"); - SetupPagesOfResponses("proj1", numVulnerabilities, "VULNERABILITY"); + SetupPagesOfResponses("proj1", numCodeSmells, "CODE_SMELL"); + SetupPagesOfResponses("proj1", numBugs, "BUG"); - var result = await service.GetSuppressedIssuesAsync("proj1", null, null, CancellationToken.None); + var result = await service.GetSuppressedIssuesAsync("proj1", null, null, CancellationToken.None); - result.Should().HaveCount( - Math.Min(MaxAllowedIssues, numCodeSmells) + - Math.Min(MaxAllowedIssues, numBugs) + - Math.Min(MaxAllowedIssues, numVulnerabilities)); + result.Should().HaveCount( + Math.Min(MaxAllowedIssues, numCodeSmells) + + Math.Min(MaxAllowedIssues, numBugs)); - DumpWarningsToConsole(); + DumpWarningsToConsole(); - messageHandler.VerifyAll(); + messageHandler.VerifyAll(); - checkForExpectedWarning(numCodeSmells, "code smells"); - checkForExpectedWarning(numBugs, "bugs"); - checkForExpectedWarning(numVulnerabilities, "vulnerabilities"); - } + checkForExpectedWarning(numCodeSmells, "code smells"); + checkForExpectedWarning(numBugs, "bugs"); + } - [TestMethod] - [DataRow("")] - [DataRow(null)] - public async Task GetSuppressedIssuesAsync_From_7_20_BranchIsNotSpecified_BranchIsNotIncludedInQueryString(string emptyBranch) - { - await ConnectToSonarQube("7.2.0.0"); - messageHandler.Reset(); + [TestMethod] + [DataRow("")] + [DataRow(null)] + public async Task GetSuppressedIssuesAsync_From_7_20_BranchIsNotSpecified_BranchIsNotIncludedInQueryString(string emptyBranch) + { + await ConnectToSonarQube("7.2.0.0"); + messageHandler.Reset(); - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetSuppressedIssuesAsync("any", emptyBranch, null, CancellationToken.None); + SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); + _ = await service.GetSuppressedIssuesAsync("any", emptyBranch, null, CancellationToken.None); - // Branch is null/empty => should not be passed - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().HaveCount(3); - actualRequests.Should().NotContain(x => x.RequestUri.Query.Contains("branch")); - } + // Branch is null/empty => should not be passed + var actualRequests = messageHandler.GetSendAsyncRequests(); + actualRequests.Should().HaveCount(2); + actualRequests.Should().NotContain(x => x.RequestUri.Query.Contains("branch")); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20_BranchIsSpecified_BranchIncludedInQueryString() - { - await ConnectToSonarQube("7.2.0.0"); - messageHandler.Reset(); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20_BranchIsSpecified_BranchIncludedInQueryString() + { + await ConnectToSonarQube("7.2.0.0"); + messageHandler.Reset(); - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetSuppressedIssuesAsync("any", "aBranch", null, CancellationToken.None); + SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); + _ = await service.GetSuppressedIssuesAsync("any", "aBranch", null, CancellationToken.None); - // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().HaveCount(3); - actualRequests.Should().OnlyContain(x => x.RequestUri.Query.Contains("&branch=aBranch&")); - } + // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities + var actualRequests = messageHandler.GetSendAsyncRequests(); + actualRequests.Should().HaveCount(2); + actualRequests.Should().OnlyContain(x => x.RequestUri.Query.Contains("&branch=aBranch&")); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20_IssueKeysAreNotSpecified_IssueKeysAreNotIncludedInQueryString() - { - await ConnectToSonarQube("7.2.0.0"); - messageHandler.Reset(); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20_IssueKeysAreNotSpecified_IssueKeysAreNotIncludedInQueryString() + { + await ConnectToSonarQube("7.2.0.0"); + messageHandler.Reset(); - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetSuppressedIssuesAsync("any", null, null, CancellationToken.None); + SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); + _ = await service.GetSuppressedIssuesAsync("any", null, null, CancellationToken.None); - // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().HaveCount(3); - actualRequests.Should().NotContain(x => x.RequestUri.Query.Contains("issues")); - } + // The wrapper is expected to make three calls, for code smells, bugs, then vulnerabilities + var actualRequests = messageHandler.GetSendAsyncRequests(); + actualRequests.Should().HaveCount(2); + actualRequests.Should().NotContain(x => x.RequestUri.Query.Contains("issues")); + } - [TestMethod] - public async Task GetSuppressedIssuesAsync_From_7_20_IssueKeysAreSpecified_IssueKeysAreIncludedInQueryString() - { - await ConnectToSonarQube("7.2.0.0"); - messageHandler.Reset(); + [TestMethod] + public async Task GetSuppressedIssuesAsync_From_7_20_IssueKeysAreSpecified_IssueKeysAreIncludedInQueryString() + { + await ConnectToSonarQube("7.2.0.0"); + messageHandler.Reset(); - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetSuppressedIssuesAsync("any", null, new[] { "issue1", "issue2" }, CancellationToken.None); + SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); + _ = await service.GetSuppressedIssuesAsync("any", null, new[] { "issue1", "issue2" }, CancellationToken.None); - // The wrapper is expected to make one call with the given issueKeys - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().ContainSingle(); - actualRequests.Should().OnlyContain(x => x.RequestUri.Query.Contains("issues=issue1%2Cissue2")); - } + // The wrapper is expected to make one call with the given issueKeys + var actualRequests = messageHandler.GetSendAsyncRequests(); + actualRequests.Should().ContainSingle(); + actualRequests.Should().OnlyContain(x => x.RequestUri.Query.Contains("issues=issue1%2Cissue2")); + } - [TestMethod] - public void GetSuppressedIssuesAsync_NotConnected() - { - // No calls to Connect - // No need to setup request, the operation should fail + [TestMethod] + public void GetSuppressedIssuesAsync_NotConnected() + { + // No calls to Connect + // No need to setup request, the operation should fail - Func>> func = async () => - await service.GetSuppressedIssuesAsync("simplcom", null, null, CancellationToken.None); + Func>> func = async () => + await service.GetSuppressedIssuesAsync("simplcom", null, null, CancellationToken.None); - func.Should().ThrowExactly().And - .Message.Should().Be("This operation expects the service to be connected."); + func.Should().ThrowExactly().And + .Message.Should().Be("This operation expects the service to be connected."); - logger.ErrorMessages.Should().Contain("The service is expected to be connected."); - } + logger.ErrorMessages.Should().Contain("The service is expected to be connected."); } } diff --git a/src/SonarQube.Client.Tests/SonarQubeService_GetTaintVulnerabilitiesRequest.cs b/src/SonarQube.Client.Tests/SonarQubeService_GetTaintVulnerabilitiesRequest.cs deleted file mode 100644 index 6087b766a2..0000000000 --- a/src/SonarQube.Client.Tests/SonarQubeService_GetTaintVulnerabilitiesRequest.cs +++ /dev/null @@ -1,1963 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarQube.Client.Models; -using SonarQube.Client.Tests.Infra; -using static SonarQube.Client.Tests.Infra.MocksHelper; - -namespace SonarQube.Client.Tests -{ - [TestClass] - public class SonarQubeService_GetTaintVulnerabilitiesRequest : SonarQubeService_TestBase - { - [TestMethod] - public void GetTaintVulnerabilitiesAsync_NotConnected() - { - // No calls to Connect - // No need to setup request, the operation should fail - Func>> func = async () => - await service.GetTaintVulnerabilitiesAsync(It.IsAny(), It.IsAny(), CancellationToken.None); - - func.Should().ThrowExactly().And - .Message.Should().Be("This operation expects the service to be connected."); - - logger.ErrorMessages.Should().Contain("The service is expected to be connected."); - } - - [TestMethod] - public async Task GetTaintVulnerabilitiesAsync_Response_From_SonarQube() - { - await ConnectToSonarQube("8.6.0.0"); - - SetupRequest("api/issues/search?projects=shared&statuses=OPEN%2CCONFIRMED%2CREOPENED%2CRESOLVED&types=VULNERABILITY&p=1&ps=500", @" -{ - ""total"": 4, - ""p"": 1, - ""ps"": 100, - ""paging"": { - ""pageIndex"": 1, - ""pageSize"": 100, - ""total"": 4 - }, - ""effortTotal"": 72, - ""debtTotal"": 72, - ""issues"": [ - { - ""key"": ""AW0p2QsM-y65ELkujuR4"", - ""rule"": ""java:S4426"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 12, - ""hash"": ""e6162855f182ddc1652ec2cdea0a085b"", - ""textRange"": { - ""startLine"": 12, - ""endLine"": 12, - ""startOffset"": 6, - ""endOffset"": 32 - }, - ""flows"": [], - ""status"": ""REOPENED"", - ""message"": ""Use a key length of at least 2048 bits."", - ""effort"": ""2min"", - ""debt"": ""2min"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a3"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-12T13:05:53+0000"", - ""updateDate"": ""2020-08-24T09:10:42+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2Qrv-y65ELkujuR0"", - ""rule"": ""java:S3330"", - ""severity"": ""CRITICAL"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 10, - ""hash"": ""79fd1a1af84dcccce2e0ede4970663ce"", - ""textRange"": { - ""startLine"": 10, - ""endLine"": 10, - ""startOffset"": 15, - ""endOffset"": 21 - }, - ""flows"": [], - ""status"": ""OPEN"", - ""message"": ""Add the \""HttpOnly\"" cookie attribute."", - ""effort"": ""10min"", - ""debt"": ""10min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a7"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2Qpn-y65ELkujuRf"", - ""rule"": ""javasecurity:S2076"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 17, - ""hash"": ""1703916771e6abb765843e62a76fcb5a"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 6, - ""endOffset"": 40 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 8, - ""endLine"": 8, - ""startOffset"": 9, - ""endOffset"": 38 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 38, - ""endLine"": 38, - ""startOffset"": 6, - ""endOffset"": 54 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct the OS command from tainted, user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2QqO-y65ELkujuRk"", - ""rule"": ""javasecurity:S3649"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 20, - ""hash"": ""b07a403eb4e77c8d6c4b3fa5c6408064"", - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 9, - ""endLine"": 9, - ""startOffset"": 9, - ""endOffset"": 57 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 6, - ""endOffset"": 68 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 59, - ""endOffset"": 67 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 6, - ""endOffset"": 82 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 8, - ""endOffset"": 43 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 15, - ""endOffset"": 42 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 39, - ""endOffset"": 55 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 8, - ""endOffset"": 36 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 11, - ""endLine"": 11, - ""startOffset"": 27, - ""endOffset"": 50 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct SQL queries directly from tainted user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cert"", - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"", - ""sql"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0100"", - ""updateDate"": ""2019-09-16T10:35:19+1300"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - } - ], - ""components"": [ - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRO"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""Servlet.java"", - ""longName"": ""src/main/java/foo/security/injection/Servlet.java"", - ""path"": ""src/main/java/foo/security/injection/Servlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""uuid"": ""AW05pEYnBvApAtP9iYg6"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""longName"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""path"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRT"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""WritingCookieServlet.java"", - ""longName"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""path"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java"", - ""uuid"": ""AW0abn1qGHw5MqdAqloE"", - ""enabled"": true, - ""qualifier"": ""TRK"", - ""name"": ""City Tour - Java project"", - ""longName"": ""City Tour - Java project"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRX"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""KeyPairUtil.java"", - ""longName"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""path"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRJ"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""BusinessThingsUtils.java"", - ""longName"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""path"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRL"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""CommandInjectionVulnerability.java"", - ""longName"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""path"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"" - } - ], - ""rules"": [ - { - ""key"": ""java:S3330"", - ""name"": ""Creating cookies without the \""HttpOnly\"" flag is security-sensitive"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S3649"", - ""name"": ""Database queries should not be vulnerable to injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S2076"", - ""name"": ""OS commands should not be vulnerable to command injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""java:S4426"", - ""name"": ""Cryptographic keys should be robust"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - } - ] -} -"); - - var result = await service.GetTaintVulnerabilitiesAsync("shared", null, CancellationToken.None); - - messageHandler.VerifyAll(); - secondaryIssueHashUpdater.Verify(x => x.UpdateHashesAsync(result, service, It.IsAny())); - - // should return only 2 taint out of 4 vulnerabilities - result.Should().HaveCount(2); - - var taint1 = result[0]; - - taint1.IssueKey.Should().Be("AW0p2Qpn-y65ELkujuRf"); - taint1.RuleId.Should().Be("javasecurity:S2076"); - taint1.Message.Should().Be("Refactor this code to not construct the OS command from tainted, user-controlled data."); - taint1.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Hash.Should().Be("1703916771e6abb765843e62a76fcb5a"); - taint1.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0000")); - taint1.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:29:47+0000")); - taint1.Flows.Should().NotBeEmpty(); - taint1.Flows.Count.Should().Be(1); - taint1.Flows[0].Locations.Count.Should().Be(8); - taint1.Flows[0].Locations[0].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Flows[0].Locations[0].TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - taint1.Flows[0].Locations[0].Message.Should().Be("tainted value is used to perform a security-sensitive operation"); - taint1.Flows[0].Locations[3].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint1.Flows[0].Locations[3].TextRange.Should().BeEquivalentTo(new IssueTextRange(38, 38, 6, 54)); - taint1.Flows[0].Locations[3].Message.Should().Be("taint value is propagated"); - taint1.TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - - var taint2 = result[1]; - - taint2.IssueKey.Should().Be("AW0p2QqO-y65ELkujuRk"); - taint2.RuleId.Should().Be("javasecurity:S3649"); - taint2.Message.Should().Be("Refactor this code to not construct SQL queries directly from tainted user-controlled data."); - taint2.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\SQLInjectionVulnerabilityCollectionMultipleFiles.java"); - taint2.Hash.Should().Be("b07a403eb4e77c8d6c4b3fa5c6408064"); - taint2.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0100")); - taint2.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-16T10:35:19+1300")); - taint2.Flows.Should().NotBeEmpty(); - taint2.Flows.Count.Should().Be(1); - taint2.Flows[0].Locations.Count.Should().Be(16); - taint2.Flows[0].Locations[7].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\BusinessThingsUtils.java"); - taint2.Flows[0].Locations[7].TextRange.Should().BeEquivalentTo(new IssueTextRange(18, 18, 15, 42)); - taint2.Flows[0].Locations[7].Message.Should().Be("taint value is propagated"); - taint2.Flows[0].Locations[15].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint2.Flows[0].Locations[15].TextRange.Should().BeEquivalentTo(new IssueTextRange(34, 34, 20, 59)); - taint2.Flows[0].Locations[15].Message.Should().Be("this value can be controlled by the user"); - taint2.TextRange.Should().BeEquivalentTo(new IssueTextRange(20, 20, 6, 23)); - } - - [TestMethod] - public async Task GetTaintVulnerabilitiesWithContextAsync_Response_From_SonarQube() - { - await ConnectToSonarQube("9.6.0.0"); - - SetupRequest("api/issues/search?additionalFields=ruleDescriptionContextKey&projects=shared&statuses=OPEN%2CCONFIRMED%2CREOPENED%2CRESOLVED&types=VULNERABILITY&p=1&ps=500", @" -{ - ""total"": 4, - ""p"": 1, - ""ps"": 100, - ""paging"": { - ""pageIndex"": 1, - ""pageSize"": 100, - ""total"": 4 - }, - ""effortTotal"": 72, - ""debtTotal"": 72, - ""issues"": [ - { - ""key"": ""AW0p2QsM-y65ELkujuR4"", - ""rule"": ""java:S4426"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 12, - ""hash"": ""e6162855f182ddc1652ec2cdea0a085b"", - ""textRange"": { - ""startLine"": 12, - ""endLine"": 12, - ""startOffset"": 6, - ""endOffset"": 32 - }, - ""flows"": [], - ""status"": ""REOPENED"", - ""message"": ""Use a key length of at least 2048 bits."", - ""effort"": ""2min"", - ""debt"": ""2min"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a3"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-12T13:05:53+0000"", - ""updateDate"": ""2020-08-24T09:10:42+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2Qrv-y65ELkujuR0"", - ""rule"": ""java:S3330"", - ""severity"": ""CRITICAL"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 10, - ""hash"": ""79fd1a1af84dcccce2e0ede4970663ce"", - ""textRange"": { - ""startLine"": 10, - ""endLine"": 10, - ""startOffset"": 15, - ""endOffset"": 21 - }, - ""flows"": [], - ""status"": ""OPEN"", - ""message"": ""Add the \""HttpOnly\"" cookie attribute."", - ""effort"": ""10min"", - ""debt"": ""10min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a7"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""ruleDescriptionContextKey"": ""context"", - ""key"": ""AW0p2Qpn-y65ELkujuRf"", - ""rule"": ""javasecurity:S2076"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 17, - ""hash"": ""1703916771e6abb765843e62a76fcb5a"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 6, - ""endOffset"": 40 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 8, - ""endLine"": 8, - ""startOffset"": 9, - ""endOffset"": 38 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 38, - ""endLine"": 38, - ""startOffset"": 6, - ""endOffset"": 54 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct the OS command from tainted, user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2QqO-y65ELkujuRk"", - ""rule"": ""javasecurity:S3649"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 20, - ""hash"": ""b07a403eb4e77c8d6c4b3fa5c6408064"", - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 9, - ""endLine"": 9, - ""startOffset"": 9, - ""endOffset"": 57 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 6, - ""endOffset"": 68 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 59, - ""endOffset"": 67 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 6, - ""endOffset"": 82 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 8, - ""endOffset"": 43 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 15, - ""endOffset"": 42 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 39, - ""endOffset"": 55 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 8, - ""endOffset"": 36 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 11, - ""endLine"": 11, - ""startOffset"": 27, - ""endOffset"": 50 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct SQL queries directly from tainted user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cert"", - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"", - ""sql"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0100"", - ""updateDate"": ""2019-09-16T10:35:19+1300"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - } - ], - ""components"": [ - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRO"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""Servlet.java"", - ""longName"": ""src/main/java/foo/security/injection/Servlet.java"", - ""path"": ""src/main/java/foo/security/injection/Servlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""uuid"": ""AW05pEYnBvApAtP9iYg6"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""longName"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""path"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRT"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""WritingCookieServlet.java"", - ""longName"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""path"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java"", - ""uuid"": ""AW0abn1qGHw5MqdAqloE"", - ""enabled"": true, - ""qualifier"": ""TRK"", - ""name"": ""City Tour - Java project"", - ""longName"": ""City Tour - Java project"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRX"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""KeyPairUtil.java"", - ""longName"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""path"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRJ"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""BusinessThingsUtils.java"", - ""longName"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""path"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRL"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""CommandInjectionVulnerability.java"", - ""longName"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""path"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"" - } - ], - ""rules"": [ - { - ""key"": ""java:S3330"", - ""name"": ""Creating cookies without the \""HttpOnly\"" flag is security-sensitive"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S3649"", - ""name"": ""Database queries should not be vulnerable to injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S2076"", - ""name"": ""OS commands should not be vulnerable to command injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""java:S4426"", - ""name"": ""Cryptographic keys should be robust"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - } - ] -} -"); - - var result = await service.GetTaintVulnerabilitiesAsync("shared", null, CancellationToken.None); - - messageHandler.VerifyAll(); - secondaryIssueHashUpdater.Verify(x => x.UpdateHashesAsync(result, service, It.IsAny())); - - // should return only 2 taint out of 4 vulnerabilities - result.Should().HaveCount(2); - - var taint1 = result[0]; - - taint1.IssueKey.Should().Be("AW0p2Qpn-y65ELkujuRf"); - taint1.RuleId.Should().Be("javasecurity:S2076"); - taint1.Message.Should().Be("Refactor this code to not construct the OS command from tainted, user-controlled data."); - taint1.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Hash.Should().Be("1703916771e6abb765843e62a76fcb5a"); - taint1.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0000")); - taint1.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:29:47+0000")); - taint1.Flows.Should().NotBeEmpty(); - taint1.Flows.Count.Should().Be(1); - taint1.Flows[0].Locations.Count.Should().Be(8); - taint1.Flows[0].Locations[0].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Flows[0].Locations[0].TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - taint1.Flows[0].Locations[0].Message.Should().Be("tainted value is used to perform a security-sensitive operation"); - taint1.Flows[0].Locations[3].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint1.Flows[0].Locations[3].TextRange.Should().BeEquivalentTo(new IssueTextRange(38, 38, 6, 54)); - taint1.Flows[0].Locations[3].Message.Should().Be("taint value is propagated"); - taint1.TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - taint1.Context.Should().Be("context"); - - var taint2 = result[1]; - - taint2.IssueKey.Should().Be("AW0p2QqO-y65ELkujuRk"); - taint2.RuleId.Should().Be("javasecurity:S3649"); - taint2.Message.Should().Be("Refactor this code to not construct SQL queries directly from tainted user-controlled data."); - taint2.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\SQLInjectionVulnerabilityCollectionMultipleFiles.java"); - taint2.Hash.Should().Be("b07a403eb4e77c8d6c4b3fa5c6408064"); - taint2.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0100")); - taint2.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-16T10:35:19+1300")); - taint2.Flows.Should().NotBeEmpty(); - taint2.Flows.Count.Should().Be(1); - taint2.Flows[0].Locations.Count.Should().Be(16); - taint2.Flows[0].Locations[7].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\BusinessThingsUtils.java"); - taint2.Flows[0].Locations[7].TextRange.Should().BeEquivalentTo(new IssueTextRange(18, 18, 15, 42)); - taint2.Flows[0].Locations[7].Message.Should().Be("taint value is propagated"); - taint2.Flows[0].Locations[15].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint2.Flows[0].Locations[15].TextRange.Should().BeEquivalentTo(new IssueTextRange(34, 34, 20, 59)); - taint2.Flows[0].Locations[15].Message.Should().Be("this value can be controlled by the user"); - taint2.TextRange.Should().BeEquivalentTo(new IssueTextRange(20, 20, 6, 23)); - taint2.Context.Should().BeNull(); - } - - [TestMethod] - public async Task GetTaintVulnerabilitiesWithCCTAsync_Response_From_SonarQube() - { - await ConnectToSonarQube("10.2.0.0"); - - SetupRequest("api/issues/search?additionalFields=ruleDescriptionContextKey&projects=shared&statuses=OPEN%2CCONFIRMED%2CREOPENED%2CRESOLVED&types=VULNERABILITY&p=1&ps=500", @" -{ - ""total"": 4, - ""p"": 1, - ""ps"": 100, - ""paging"": { - ""pageIndex"": 1, - ""pageSize"": 100, - ""total"": 4 - }, - ""effortTotal"": 72, - ""debtTotal"": 72, - ""issues"": [ - { - ""key"": ""AW0p2QsM-y65ELkujuR4"", - ""rule"": ""java:S4426"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 12, - ""hash"": ""e6162855f182ddc1652ec2cdea0a085b"", - ""cleanCodeAttribute"": ""CLEAR"", - ""cleanCodeAttributeCategory"": ""INTENTIONAL"", - ""impacts"": [ - { - ""softwareQuality"": ""MAINTAINABILITY"", - ""severity"": ""HIGH"" - } - ], - ""textRange"": { - ""startLine"": 12, - ""endLine"": 12, - ""startOffset"": 6, - ""endOffset"": 32 - }, - ""flows"": [], - ""status"": ""REOPENED"", - ""message"": ""Use a key length of at least 2048 bits."", - ""effort"": ""2min"", - ""debt"": ""2min"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a3"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-12T13:05:53+0000"", - ""updateDate"": ""2020-08-24T09:10:42+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2Qrv-y65ELkujuR0"", - ""rule"": ""java:S3330"", - ""severity"": ""CRITICAL"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 10, - ""hash"": ""79fd1a1af84dcccce2e0ede4970663ce"", - ""cleanCodeAttribute"": ""CLEAR"", - ""cleanCodeAttributeCategory"": ""INTENTIONAL"", - ""impacts"": [ - { - ""softwareQuality"": ""MAINTAINABILITY"", - ""severity"": ""HIGH"" - } - ], - ""textRange"": { - ""startLine"": 10, - ""endLine"": 10, - ""startOffset"": 15, - ""endOffset"": 21 - }, - ""flows"": [], - ""status"": ""OPEN"", - ""message"": ""Add the \""HttpOnly\"" cookie attribute."", - ""effort"": ""10min"", - ""debt"": ""10min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a7"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""ruleDescriptionContextKey"": ""context"", - ""key"": ""AW0p2Qpn-y65ELkujuRf"", - ""rule"": ""javasecurity:S2076"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 17, - ""hash"": ""1703916771e6abb765843e62a76fcb5a"", - ""cleanCodeAttribute"": ""CLEAR"", - ""cleanCodeAttributeCategory"": ""INTENTIONALITY"", - ""impacts"": [ - { - ""softwareQuality"": ""MAINTAINABILITY"", - ""severity"": ""HIGH"" - }, - { - ""softwareQuality"": ""SECURITY"", - ""severity"": ""HIGH"" - } - ], - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 4, - ""endOffset"": 25 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 6, - ""endOffset"": 40 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""textRange"": { - ""startLine"": 8, - ""endLine"": 8, - ""startOffset"": 9, - ""endOffset"": 38 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 38, - ""endLine"": 38, - ""startOffset"": 6, - ""endOffset"": 54 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct the OS command from tainted, user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0000"", - ""updateDate"": ""2019-09-11T12:29:47+0000"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - }, - { - ""key"": ""AW0p2QqO-y65ELkujuRk"", - ""rule"": ""javasecurity:S3649"", - ""severity"": ""BLOCKER"", - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""project"": ""com.sonarsource:citytour2019-java"", - ""line"": 20, - ""hash"": ""b07a403eb4e77c8d6c4b3fa5c6408064"", - ""cleanCodeAttribute"": ""DISTINCT"", - ""cleanCodeAttributeCategory"": ""ADAPTABILITY"", - ""impacts"": [ - { - ""softwareQuality"": ""RELIABILITY"", - ""severity"": ""LOW"" - }, - { - ""softwareQuality"": ""SECURITY"", - ""severity"": ""HIGH"" - } - ], - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""flows"": [ - { - ""locations"": [ - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 20, - ""endLine"": 20, - ""startOffset"": 6, - ""endOffset"": 23 - }, - ""msg"": ""tainted value is used to perform a security-sensitive operation"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""textRange"": { - ""startLine"": 9, - ""endLine"": 9, - ""startOffset"": 9, - ""endOffset"": 57 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 6, - ""endOffset"": 68 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 43, - ""endLine"": 43, - ""startOffset"": 59, - ""endOffset"": 67 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 6, - ""endOffset"": 82 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 8, - ""endOffset"": 43 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 18, - ""endLine"": 18, - ""startOffset"": 15, - ""endOffset"": 42 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 17, - ""endLine"": 17, - ""startOffset"": 39, - ""endOffset"": 55 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 14, - ""endLine"": 14, - ""startOffset"": 8, - ""endOffset"": 36 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""textRange"": { - ""startLine"": 11, - ""endLine"": 11, - ""startOffset"": 27, - ""endOffset"": 50 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 41, - ""endLine"": 41, - ""startOffset"": 23, - ""endOffset"": 81 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 6, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 36, - ""endLine"": 36, - ""startOffset"": 22, - ""endOffset"": 72 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 4, - ""endOffset"": 59 - }, - ""msg"": ""taint value is propagated"" - }, - { - ""component"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""textRange"": { - ""startLine"": 34, - ""endLine"": 34, - ""startOffset"": 20, - ""endOffset"": 59 - }, - ""msg"": ""this value can be controlled by the user"" - } - ] - } - ], - ""status"": ""OPEN"", - ""message"": ""Refactor this code to not construct SQL queries directly from tainted user-controlled data."", - ""effort"": ""30min"", - ""debt"": ""30min"", - ""assignee"": ""agigleux@github"", - ""author"": ""alexandre.gigleux@sonarsource.com"", - ""tags"": [ - ""cert"", - ""cwe"", - ""owasp-a1"", - ""sans-top25-insecure"", - ""sql"" - ], - ""transitions"": [ - ""confirm"", - ""resolve"", - ""falsepositive"", - ""wontfix"" - ], - ""actions"": [ - ""set_type"", - ""set_tags"", - ""comment"", - ""set_severity"", - ""assign"" - ], - ""comments"": [], - ""creationDate"": ""2019-09-11T12:25:08+0100"", - ""updateDate"": ""2019-09-16T10:35:19+1300"", - ""type"": ""VULNERABILITY"", - ""organization"": ""default-organization"", - ""scope"": ""MAIN"" - } - ], - ""components"": [ - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/Servlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRO"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""Servlet.java"", - ""longName"": ""src/main/java/foo/security/injection/Servlet.java"", - ""path"": ""src/main/java/foo/security/injection/Servlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""uuid"": ""AW05pEYnBvApAtP9iYg6"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""longName"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"", - ""path"": ""src/main/java/foo/security/injection/SQLInjectionVulnerabilityCollectionMultipleFiles.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRT"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""WritingCookieServlet.java"", - ""longName"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"", - ""path"": ""src/main/java/foo/security/hotspots/WritingCookieServlet.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java"", - ""uuid"": ""AW0abn1qGHw5MqdAqloE"", - ""enabled"": true, - ""qualifier"": ""TRK"", - ""name"": ""City Tour - Java project"", - ""longName"": ""City Tour - Java project"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRX"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""KeyPairUtil.java"", - ""longName"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"", - ""path"": ""src/main/java/foo/security/crypto/KeyPairUtil.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRJ"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""BusinessThingsUtils.java"", - ""longName"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"", - ""path"": ""src/main/java/foo/security/injection/BusinessThingsUtils.java"" - }, - { - ""organization"": ""default-organization"", - ""key"": ""com.sonarsource:citytour2019-java:src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""uuid"": ""AW0p2QjJ-y65ELkujuRL"", - ""enabled"": true, - ""qualifier"": ""FIL"", - ""name"": ""CommandInjectionVulnerability.java"", - ""longName"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"", - ""path"": ""src/main/java/foo/security/injection/CommandInjectionVulnerability.java"" - } - ], - ""rules"": [ - { - ""key"": ""java:S3330"", - ""name"": ""Creating cookies without the \""HttpOnly\"" flag is security-sensitive"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S3649"", - ""name"": ""Database queries should not be vulnerable to injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""javasecurity:S2076"", - ""name"": ""OS commands should not be vulnerable to command injection attacks"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - }, - { - ""key"": ""java:S4426"", - ""name"": ""Cryptographic keys should be robust"", - ""lang"": ""java"", - ""status"": ""READY"", - ""langName"": ""Java"" - } - ] -} -"); - - var result = await service.GetTaintVulnerabilitiesAsync("shared", null, CancellationToken.None); - - messageHandler.VerifyAll(); - secondaryIssueHashUpdater.Verify(x => x.UpdateHashesAsync(result, service, It.IsAny())); - - // should return only 2 taint out of 4 vulnerabilities - result.Should().HaveCount(2); - - var taint1 = result[0]; - - var taint1Impacts = new Dictionary - { - { SonarQubeSoftwareQuality.Maintainability, SonarQubeSoftwareQualitySeverity.High }, - { SonarQubeSoftwareQuality.Security, SonarQubeSoftwareQualitySeverity.High } - }; - - taint1.IssueKey.Should().Be("AW0p2Qpn-y65ELkujuRf"); - taint1.RuleId.Should().Be("javasecurity:S2076"); - taint1.Message.Should().Be("Refactor this code to not construct the OS command from tainted, user-controlled data."); - taint1.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Hash.Should().Be("1703916771e6abb765843e62a76fcb5a"); - taint1.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0000")); - taint1.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:29:47+0000")); - taint1.Flows.Should().NotBeEmpty(); - taint1.Flows.Count.Should().Be(1); - taint1.Flows[0].Locations.Count.Should().Be(8); - taint1.Flows[0].Locations[0].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\CommandInjectionVulnerability.java"); - taint1.Flows[0].Locations[0].TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - taint1.Flows[0].Locations[0].Message.Should().Be("tainted value is used to perform a security-sensitive operation"); - taint1.Flows[0].Locations[3].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint1.Flows[0].Locations[3].TextRange.Should().BeEquivalentTo(new IssueTextRange(38, 38, 6, 54)); - taint1.Flows[0].Locations[3].Message.Should().Be("taint value is propagated"); - taint1.TextRange.Should().BeEquivalentTo(new IssueTextRange(17, 17, 4, 25)); - taint1.Context.Should().Be("context"); - taint1.CleanCodeAttribute.Should().Be(SonarQubeCleanCodeAttribute.Clear); - taint1.DefaultImpacts.Should().BeEquivalentTo(taint1Impacts); - - var taint2 = result[1]; - - var taint2Impacts = new Dictionary - { - { SonarQubeSoftwareQuality.Reliability, SonarQubeSoftwareQualitySeverity.Low }, - { SonarQubeSoftwareQuality.Security, SonarQubeSoftwareQualitySeverity.High } - }; - - taint2.IssueKey.Should().Be("AW0p2QqO-y65ELkujuRk"); - taint2.RuleId.Should().Be("javasecurity:S3649"); - taint2.Message.Should().Be("Refactor this code to not construct SQL queries directly from tainted user-controlled data."); - taint2.FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\SQLInjectionVulnerabilityCollectionMultipleFiles.java"); - taint2.Hash.Should().Be("b07a403eb4e77c8d6c4b3fa5c6408064"); - taint2.CreationTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-11T12:25:08+0100")); - taint2.LastUpdateTimestamp.Should().Be(DateTimeOffset.Parse("2019-09-16T10:35:19+1300")); - taint2.Flows.Should().NotBeEmpty(); - taint2.Flows.Count.Should().Be(1); - taint2.Flows[0].Locations.Count.Should().Be(16); - taint2.Flows[0].Locations[7].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\BusinessThingsUtils.java"); - taint2.Flows[0].Locations[7].TextRange.Should().BeEquivalentTo(new IssueTextRange(18, 18, 15, 42)); - taint2.Flows[0].Locations[7].Message.Should().Be("taint value is propagated"); - taint2.Flows[0].Locations[15].FilePath.Should().Be("src\\main\\java\\foo\\security\\injection\\Servlet.java"); - taint2.Flows[0].Locations[15].TextRange.Should().BeEquivalentTo(new IssueTextRange(34, 34, 20, 59)); - taint2.Flows[0].Locations[15].Message.Should().Be("this value can be controlled by the user"); - taint2.TextRange.Should().BeEquivalentTo(new IssueTextRange(20, 20, 6, 23)); - taint2.Context.Should().BeNull(); - taint2.CleanCodeAttribute.Should().Be(SonarQubeCleanCodeAttribute.Distinct); - taint2.DefaultImpacts.Should().BeEquivalentTo(taint2Impacts); - } - - [TestMethod] - [DataRow("")] - [DataRow(null)] - public async Task GetTaintVulnerabilitiesAsync_BranchIsNotSpecified_BranchIsNotIncludedInQueryString(string emptyBranch) - { - await ConnectToSonarQube("8.6.0.0"); - messageHandler.Reset(); - - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetTaintVulnerabilitiesAsync("any", emptyBranch, CancellationToken.None); - - // Branch is null/empty => should not be passed - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().ContainSingle(); - actualRequests[0].RequestUri.Query.Contains("branch").Should().BeFalse(); - } - - [TestMethod] - public async Task GetTaintVulnerabilitiesAsync_BranchIsSpecified_BranchIncludedInQueryString() - { - await ConnectToSonarQube("8.6.0.0"); - messageHandler.Reset(); - - SetupHttpRequest(messageHandler, EmptyGetIssuesResponse); - _ = await service.GetTaintVulnerabilitiesAsync("any", "aBranch", CancellationToken.None); - - var actualRequests = messageHandler.GetSendAsyncRequests(); - actualRequests.Should().ContainSingle(); - actualRequests[0].RequestUri.Query.Contains("&branch=aBranch&").Should().BeTrue(); - } - } -} diff --git a/src/SonarQube.Client/Api/DefaultConfiguration.cs b/src/SonarQube.Client/Api/DefaultConfiguration.cs index 638088b768..df4278c584 100644 --- a/src/SonarQube.Client/Api/DefaultConfiguration.cs +++ b/src/SonarQube.Client/Api/DefaultConfiguration.cs @@ -20,86 +20,81 @@ using SonarQube.Client.Requests; -namespace SonarQube.Client.Api +namespace SonarQube.Client.Api; + +internal static class DefaultConfiguration { - internal static class DefaultConfiguration + public static RequestFactory ConfigureSonarQube(RequestFactory requestFactory) { - public static RequestFactory ConfigureSonarQube(RequestFactory requestFactory) - { - requestFactory - .RegisterRequest("2.1") - .RegisterRequest("2.1") - .RegisterRequest("2.1") - .RegisterRequest("2.6") - .RegisterRequest("3.3") - .RegisterRequest("5.0") - .RegisterRequest("5.1") - .RegisterRequest("5.1") - .RegisterRequest("5.2") - .RegisterRequest("5.2") - .RegisterRequest("5.2") - .RegisterRequest("5.4") - .RegisterRequest("5.5") - .RegisterRequest("5.5") - .RegisterRequest("6.2") - .RegisterRequest("6.2") - .RegisterRequest("6.3") - .RegisterRequest("6.3") - .RegisterRequest("6.5") - .RegisterRequest("6.5") - .RegisterRequest("6.6") - .RegisterRequest("6.6") - .RegisterRequest("6.6") - .RegisterRequest("7.0") - .RegisterRequest>("7.2") - .RegisterRequest("8.6") - .RegisterRequest("8.6") - .RegisterRequest("7.2") - .RegisterRequest("9.4") - .RegisterRequest("9.5") - .RegisterRequest("9.6") - .RegisterRequest("9.6") - .RegisterRequest("9.7") - .RegisterRequest("10.2") - .RegisterRequest("10.2") - .RegisterRequest("10.2") - .RegisterRequest("9.9") - .RegisterRequest("10.4") - .RegisterRequest("9.9") - .RegisterRequest("9.9"); + requestFactory + .RegisterRequest("2.1") + .RegisterRequest("2.1") + .RegisterRequest("2.1") + .RegisterRequest("2.6") + .RegisterRequest("3.3") + .RegisterRequest("5.0") + .RegisterRequest("5.1") + .RegisterRequest("5.1") + .RegisterRequest("5.2") + .RegisterRequest("5.2") + .RegisterRequest("5.2") + .RegisterRequest("5.4") + .RegisterRequest("5.5") + .RegisterRequest("5.5") + .RegisterRequest("6.2") + .RegisterRequest("6.2") + .RegisterRequest("6.3") + .RegisterRequest("6.3") + .RegisterRequest("6.5") + .RegisterRequest("6.5") + .RegisterRequest("6.6") + .RegisterRequest("6.6") + .RegisterRequest("6.6") + .RegisterRequest("7.0") + .RegisterRequest>("7.2") + .RegisterRequest("8.6") + .RegisterRequest("7.2") + .RegisterRequest("9.4") + .RegisterRequest("9.5") + .RegisterRequest("9.6") + .RegisterRequest("9.7") + .RegisterRequest("10.2") + .RegisterRequest("10.2") + .RegisterRequest("9.9") + .RegisterRequest("10.4") + .RegisterRequest("9.9") + .RegisterRequest("9.9"); - return requestFactory; - } + return requestFactory; + } - public static UnversionedRequestFactory ConfigureSonarCloud(UnversionedRequestFactory requestFactory) - { - requestFactory - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest>() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest() - .RegisterRequest(); + public static UnversionedRequestFactory ConfigureSonarCloud(UnversionedRequestFactory requestFactory) + { + requestFactory + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest>() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest() + .RegisterRequest(); - return requestFactory; - } + return requestFactory; } } diff --git a/src/SonarQube.Client/Api/IGetIssuesRequest.cs b/src/SonarQube.Client/Api/IGetIssuesRequest.cs index 06fb141ab6..6df278d088 100644 --- a/src/SonarQube.Client/Api/IGetIssuesRequest.cs +++ b/src/SonarQube.Client/Api/IGetIssuesRequest.cs @@ -21,28 +21,25 @@ using SonarQube.Client.Models; using SonarQube.Client.Requests; -namespace SonarQube.Client.Api -{ - interface IGetIssuesRequest : IRequest - { - string ProjectKey { get; set; } +namespace SonarQube.Client.Api; - string Statuses { get; set; } +interface IGetIssuesRequest : IRequest +{ + string ProjectKey { get; set; } - /// - /// The branch name to fetch. - /// - /// If the value is null/empty, the main branch will be fetched - string Branch { get; set; } + string Statuses { get; set; } - string[] IssueKeys { get; set; } + /// + /// The branch name to fetch. + /// + /// If the value is null/empty, the main branch will be fetched + string Branch { get; set; } - string RuleId { get; set; } + string[] IssueKeys { get; set; } - string ComponentKey { get; set; } + string RuleId { get; set; } - bool IncludeTaint { get; set; } + string ComponentKey { get; set; } - // Update when adding properties here. - } + // Update when adding properties here. } diff --git a/src/SonarQube.Client/Api/IGetTaintVulnerabilitiesRequest.cs b/src/SonarQube.Client/Api/IGetTaintVulnerabilitiesRequest.cs deleted file mode 100644 index 845f9ed8dc..0000000000 --- a/src/SonarQube.Client/Api/IGetTaintVulnerabilitiesRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using SonarQube.Client.Models; -using SonarQube.Client.Requests; - -namespace SonarQube.Client.Api -{ - /// - /// Returns taint vulnerabilities that are in status OPEN, CONFIRMED, or REOPENED. - /// - public interface IGetTaintVulnerabilitiesRequest : IRequest - { - string ProjectKey { get; set; } - - /// - /// The branch name to fetch. - /// - /// If the value is null/empty, the main branch will be fetched - string Branch { get; set; } - } -} diff --git a/src/SonarQube.Client/Api/V10_2/GetTaintVulnerabilitiesWithCCTRequest.cs b/src/SonarQube.Client/Api/V10_2/GetTaintVulnerabilitiesWithCCTRequest.cs deleted file mode 100644 index 97d049b14f..0000000000 --- a/src/SonarQube.Client/Api/V10_2/GetTaintVulnerabilitiesWithCCTRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using SonarQube.Client.Api.V8_6; -using SonarQube.Client.Models; - -namespace SonarQube.Client.Api.V10_2 -{ - internal class GetTaintVulnerabilitiesWithCCTRequest : GetTaintVulnerabilitiesRequest - { - public override async Task InvokeAsync(HttpClient httpClient, CancellationToken token) - { - getIssuesRequest = new GetIssuesWithCCTRequest(); - - return await base.InvokeAsync(httpClient, token); - } - } -} diff --git a/src/SonarQube.Client/Api/V5_10/GetIssuesRequest.cs b/src/SonarQube.Client/Api/V5_10/GetIssuesRequest.cs index 699e7064a5..82e887aa98 100644 --- a/src/SonarQube.Client/Api/V5_10/GetIssuesRequest.cs +++ b/src/SonarQube.Client/Api/V5_10/GetIssuesRequest.cs @@ -18,13 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Collections.Generic; using System.ComponentModel; using System.IO; -using System.Linq; using System.Net.Http; -using System.Threading.Tasks; using Google.Protobuf; using Newtonsoft.Json; using SonarQube.Client.Helpers; @@ -32,83 +28,79 @@ using SonarQube.Client.Models; using SonarQube.Client.Requests; -namespace SonarQube.Client.Api.V5_10 -{ - public class GetIssuesRequest : RequestBase, IGetIssuesRequest - { - private HashSet statuses = new HashSet(); // Prevent null reference exceptions when Statuses is never set +namespace SonarQube.Client.Api.V5_10; - [JsonProperty("key")] - public virtual string ProjectKey { get; set; } +public class GetIssuesRequest : RequestBase, IGetIssuesRequest +{ + private HashSet statuses = new HashSet(); // Prevent null reference exceptions when Statuses is never set - [JsonProperty("statuses", DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)] - public string Statuses - { - get { return null; } // Always return null to prevent this value from serialization - set { statuses = new HashSet(value.Split(',')); } - } + [JsonProperty("key")] + public virtual string ProjectKey { get; set; } - [JsonIgnore] // We don't support the branch parameter in v5.10 - public string Branch { get; set; } + [JsonProperty("statuses", DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue(null)] + public string Statuses + { + get { return null; } // Always return null to prevent this value from serialization + set { statuses = new HashSet(value.Split(',')); } + } - [JsonIgnore] // We decided not to support it for API calls older than v7.20 - public string[] IssueKeys { get; set; } + [JsonIgnore] // We don't support the branch parameter in v5.10 + public string Branch { get; set; } - protected override string Path => "batch/issues"; + [JsonIgnore] // We decided not to support it for API calls older than v7.20 + public string[] IssueKeys { get; set; } - [JsonIgnore] // We decided not to support it for API calls older than v7.20 - public string RuleId { get; set; } + protected override string Path => "batch/issues"; - [JsonIgnore] // We decided not to support it for API calls older than v7.20 - public string ComponentKey { get; set; } + [JsonIgnore] // We decided not to support it for API calls older than v7.20 + public string RuleId { get; set; } - [JsonIgnore] - public bool IncludeTaint { get; set; } = true; + [JsonIgnore] // We decided not to support it for API calls older than v7.20 + public string ComponentKey { get; set; } - protected async override Task> ReadResponseAsync(HttpResponseMessage httpResponse) + protected async override Task> ReadResponseAsync(HttpResponseMessage httpResponse) + { + if (!httpResponse.IsSuccessStatusCode) { - if (!httpResponse.IsSuccessStatusCode) - { - return new Result(httpResponse, null); - } - - var byteArray = await httpResponse.Content.ReadAsByteArrayAsync(); - // Protobuf for C# throws when trying to read outside of the buffer and ReadAsStreamAsync returns a non - // seekable stream so we can't determine when to stop. The hack is to use an intermediate MemoryStream - // so we can control when to stop reading. - // Note we might want to use FileStream instead to avoid intensive memory usage. - using (var stream = new MemoryStream(byteArray)) - { - var result = ReadFromProtobufStream(stream, ServerIssue.Parser) - // This Web API does not support server-side filtering, so we do it on the client - .Where(issue => statuses.Contains(issue.Status)) - .Select(ToSonarQubeIssue) - .ToArray(); - return new Result(httpResponse, result); - } + return new Result(httpResponse, null); } - protected override SonarQubeIssue[] ParseResponse(string response) + var byteArray = await httpResponse.Content.ReadAsByteArrayAsync(); + // Protobuf for C# throws when trying to read outside of the buffer and ReadAsStreamAsync returns a non + // seekable stream so we can't determine when to stop. The hack is to use an intermediate MemoryStream + // so we can control when to stop reading. + // Note we might want to use FileStream instead to avoid intensive memory usage. + using (var stream = new MemoryStream(byteArray)) { - throw new NotSupportedException("This method will not be called because we override ReadResponse."); + var result = ReadFromProtobufStream(stream, ServerIssue.Parser) + // This Web API does not support server-side filtering, so we do it on the client + .Where(issue => statuses.Contains(issue.Status)) + .Select(ToSonarQubeIssue) + .ToArray(); + return new Result(httpResponse, result); } + } + + protected override SonarQubeIssue[] ParseResponse(string response) + { + throw new NotSupportedException("This method will not be called because we override ReadResponse."); + } - private static IEnumerable ReadFromProtobufStream(Stream stream, MessageParser parser) - where T : IMessage + private static IEnumerable ReadFromProtobufStream(Stream stream, MessageParser parser) + where T : IMessage + { + while (stream.Position < stream.Length) { - while (stream.Position < stream.Length) - { - yield return parser.ParseDelimitedFrom(stream); - } + yield return parser.ParseDelimitedFrom(stream); } - - private static SonarQubeIssue ToSonarQubeIssue(ServerIssue issue) => - new SonarQubeIssue(issue.Key, FilePathNormalizer.NormalizeSonarQubePath(issue.Path), issue.Checksum, issue.Msg, - issue.ModuleKey, issue.RuleKey, issue.Status == "RESOLVED", - severity: SonarQubeIssueSeverity.Unknown, - creationTimestamp: DateTimeOffset.MinValue, // we don't support the timestamp fields for these versions of SQ - lastUpdateTimestamp: DateTimeOffset.MinValue, - textRange: new IssueTextRange(issue.Line, issue.Line, 0, 0), - flows: null); } + + private static SonarQubeIssue ToSonarQubeIssue(ServerIssue issue) => + new SonarQubeIssue(issue.Key, FilePathNormalizer.NormalizeSonarQubePath(issue.Path), issue.Checksum, issue.Msg, + issue.ModuleKey, issue.RuleKey, issue.Status == "RESOLVED", + severity: SonarQubeIssueSeverity.Unknown, + creationTimestamp: DateTimeOffset.MinValue, // we don't support the timestamp fields for these versions of SQ + lastUpdateTimestamp: DateTimeOffset.MinValue, + textRange: new IssueTextRange(issue.Line, issue.Line, 0, 0), + flows: null); } diff --git a/src/SonarQube.Client/Api/V7_20/GetIssuesRequest.cs b/src/SonarQube.Client/Api/V7_20/GetIssuesRequest.cs index f6b0d57c5e..80aa958d66 100644 --- a/src/SonarQube.Client/Api/V7_20/GetIssuesRequest.cs +++ b/src/SonarQube.Client/Api/V7_20/GetIssuesRequest.cs @@ -18,10 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SonarQube.Client.Api.Common; @@ -29,162 +26,158 @@ using SonarQube.Client.Models; using SonarQube.Client.Requests; -namespace SonarQube.Client.Api.V7_20 -{ - /// - /// Generic get issues class. It does not support as it is server-type-specific. - /// See and for more details. - /// - internal class GetIssuesRequest : PagedRequestBase - { - [JsonProperty("projects")] - public virtual string ProjectKey { get; set; } +namespace SonarQube.Client.Api.V7_20; - [JsonProperty("statuses")] - public string Statuses { get; set; } - - [JsonProperty("issues")] - public string IssueKeysAsString => IssueKeys == null ? null : string.Join(",", IssueKeys); +/// +/// Generic get issues class. It does not support as it is server-type-specific. +/// See and for more details. +/// +internal class GetIssuesRequest : PagedRequestBase +{ + [JsonProperty("projects")] + public virtual string ProjectKey { get; set; } - [JsonIgnore] - public string[] IssueKeys { get; set; } + [JsonProperty("statuses")] + public string Statuses { get; set; } - //These are normally comma seperated values but we need only one for now. If we need to pass more than 1 value we'll need to change the structure similar to issueKeys - //For now it's not needed. - [JsonProperty("rules")] - public string RuleId { get; set; } + [JsonProperty("issues")] + public string IssueKeysAsString => IssueKeys == null ? null : string.Join(",", IssueKeys); - [JsonIgnore] - public bool IncludeTaint { get; set; } = true; + [JsonIgnore] + public string[] IssueKeys { get; set; } - // Notes: - // 1) Branch support is not available in SQ Community edition. SQ will just ignore it. - // 2) SonarQube has supported the parameter since v6.6. However, the LTS at the point - // we added added branch-awareness to SLVS was v8.9.10. To minimise the amount of - // work on the SLVS side, we'll add branch support from SQ v7.2. - [JsonProperty("branch", DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue("")] - public string Branch { get; set; } + //These are normally comma seperated values but we need only one for now. If we need to pass more than 1 value we'll need to change the structure similar to issueKeys + //For now it's not needed. + [JsonProperty("rules")] + public string RuleId { get; set; } - // This property is not present in the IGetIssuesRequest interface, it is meant to be - // set by the GetIssuesRequestWrapper to add additional parameters to the API calls. - [JsonProperty("types")] - public string Types { get; set; } + // Notes: + // 1) Branch support is not available in SQ Community edition. SQ will just ignore it. + // 2) SonarQube has supported the parameter since v6.6. However, the LTS at the point + // we added added branch-awareness to SLVS was v8.9.10. To minimise the amount of + // work on the SLVS side, we'll add branch support from SQ v7.2. + [JsonProperty("branch", DefaultValueHandling = DefaultValueHandling.Ignore), DefaultValue("")] + public string Branch { get; set; } - protected override string Path => "api/issues/search"; + // This property is not present in the IGetIssuesRequest interface, it is meant to be + // set by the GetIssuesRequestWrapper to add additional parameters to the API calls. + [JsonProperty("types")] + public string Types { get; set; } - protected override SonarQubeIssue[] ParseResponse(string response) - { - var root = JObject.Parse(response); + protected override string Path => "api/issues/search"; - // This is a paged request so ParseResponse will be called once for each "page" - // of the response. However, we expect each page to be self-contained, so we want - // to rebuild the lookup each time. - componentKeyPathLookup = root.GetComponentKeyPathLookup(); + protected override SonarQubeIssue[] ParseResponse(string response) + { + var root = JObject.Parse(response); - return root["issues"] - .ToObject() - .Select(ToSonarQubeIssue) - .ToArray(); - } + // This is a paged request so ParseResponse will be called once for each "page" + // of the response. However, we expect each page to be self-contained, so we want + // to rebuild the lookup each time. + componentKeyPathLookup = root.GetComponentKeyPathLookup(); - #region Json data classes -> public read-only class conversion methods + return root["issues"] + .ToObject() + .Select(ToSonarQubeIssue) + .ToArray(); + } - /// - /// Lookup component key -> path for files. Each response contains normalized data, containing - /// issues and components, where each issue's "component" property points to a component with - /// the same "key". We obtain the FilePath of each issue from its corresponding component. - /// - private protected ILookup componentKeyPathLookup; + #region Json data classes -> public read-only class conversion methods - private SonarQubeIssue ToSonarQubeIssue(ServerIssue issue) => - new SonarQubeIssue(issue.IssueKey, ComputePath(issue.Component), issue.Hash, issue.Message, ComputeModuleKey(issue), - issue.CompositeRuleKey, issue.Status == "RESOLVED", - SonarQubeIssueSeverityConverter.Convert(issue.Severity), - issue.CreationDate, - issue.UpdateDate, - issue.TextRange.ToIssueTextRange(), - ToIssueFlows(issue.Flows), - issue.ContextKey); + /// + /// Lookup component key -> path for files. Each response contains normalized data, containing + /// issues and components, where each issue's "component" property points to a component with + /// the same "key". We obtain the FilePath of each issue from its corresponding component. + /// + private protected ILookup componentKeyPathLookup; - private protected string ComputePath(string component) => - FilePathNormalizer.NormalizeSonarQubePath(componentKeyPathLookup[component].FirstOrDefault() ?? string.Empty); + private SonarQubeIssue ToSonarQubeIssue(ServerIssue issue) => + new SonarQubeIssue(issue.IssueKey, ComputePath(issue.Component), issue.Hash, issue.Message, ComputeModuleKey(issue), + issue.CompositeRuleKey, issue.Status == "RESOLVED", + SonarQubeIssueSeverityConverter.Convert(issue.Severity), + issue.CreationDate, + issue.UpdateDate, + issue.TextRange.ToIssueTextRange(), + ToIssueFlows(issue.Flows), + issue.ContextKey); - private protected static string ComputeModuleKey(ServerIssue issue) => - issue.SubProject ?? issue.Component; + private protected string ComputePath(string component) => + FilePathNormalizer.NormalizeSonarQubePath(componentKeyPathLookup[component].FirstOrDefault() ?? string.Empty); - private protected List ToIssueFlows(ServerIssueFlow[] serverIssueFlows) => - serverIssueFlows?.Select(ToIssueFlow).ToList(); + private protected static string ComputeModuleKey(ServerIssue issue) => + issue.SubProject ?? issue.Component; - private protected IssueFlow ToIssueFlow(ServerIssueFlow serverIssueFlow) => - new IssueFlow(serverIssueFlow.Locations?.Select(ToIssueLocation).ToList()); + private protected List ToIssueFlows(ServerIssueFlow[] serverIssueFlows) => + serverIssueFlows?.Select(ToIssueFlow).ToList(); - private protected IssueLocation ToIssueLocation(ServerIssueLocation serverIssueLocation) => - new IssueLocation(ComputePath(serverIssueLocation.Component), serverIssueLocation.Component, serverIssueLocation.TextRange.ToIssueTextRange(), serverIssueLocation.Message); + private protected IssueFlow ToIssueFlow(ServerIssueFlow serverIssueFlow) => + new IssueFlow(serverIssueFlow.Locations?.Select(ToIssueLocation).ToList()); - #endregion Json data classes -> public read-only class conversion methods + private protected IssueLocation ToIssueLocation(ServerIssueLocation serverIssueLocation) => + new IssueLocation(ComputePath(serverIssueLocation.Component), serverIssueLocation.Component, serverIssueLocation.TextRange.ToIssueTextRange(), serverIssueLocation.Message); - #region JSON data classes + #endregion Json data classes -> public read-only class conversion methods - private protected class ServerIssue - { - [JsonProperty("key")] - public string IssueKey { get; set; } + #region JSON data classes - [JsonProperty("rule")] - public string CompositeRuleKey { get; set; } + private protected class ServerIssue + { + [JsonProperty("key")] + public string IssueKey { get; set; } - [JsonProperty("component")] - public string Component { get; set; } + [JsonProperty("rule")] + public string CompositeRuleKey { get; set; } - [JsonProperty("subProject")] - public string SubProject { get; set; } + [JsonProperty("component")] + public string Component { get; set; } - [JsonProperty("hash")] - public string Hash { get; set; } + [JsonProperty("subProject")] + public string SubProject { get; set; } - [JsonProperty("message")] - public string Message { get; set; } + [JsonProperty("hash")] + public string Hash { get; set; } - [JsonProperty("status")] - public string Status { get; set; } + [JsonProperty("message")] + public string Message { get; set; } - [JsonProperty("severity")] - public string Severity { get; set; } + [JsonProperty("status")] + public string Status { get; set; } - [JsonProperty("textRange")] - public ServerIssueTextRange TextRange { get; set; } + [JsonProperty("severity")] + public string Severity { get; set; } - [JsonProperty("creationDate")] - public virtual DateTimeOffset CreationDate { get; set; } + [JsonProperty("textRange")] + public ServerIssueTextRange TextRange { get; set; } - [JsonProperty("updateDate")] - public virtual DateTimeOffset UpdateDate { get; set; } + [JsonProperty("creationDate")] + public virtual DateTimeOffset CreationDate { get; set; } - [JsonProperty("flows")] - public ServerIssueFlow[] Flows { get; set; } + [JsonProperty("updateDate")] + public virtual DateTimeOffset UpdateDate { get; set; } - [JsonProperty("ruleDescriptionContextKey")] - public string ContextKey { get; set; } - } + [JsonProperty("flows")] + public ServerIssueFlow[] Flows { get; set; } - private protected sealed class ServerIssueFlow - { - [JsonProperty("locations")] - public ServerIssueLocation[] Locations { get; set; } - } + [JsonProperty("ruleDescriptionContextKey")] + public string ContextKey { get; set; } + } - private protected sealed class ServerIssueLocation - { - [JsonProperty("component")] - public string Component { get; set; } + private protected sealed class ServerIssueFlow + { + [JsonProperty("locations")] + public ServerIssueLocation[] Locations { get; set; } + } - [JsonProperty("textRange")] - public ServerIssueTextRange TextRange { get; set; } + private protected sealed class ServerIssueLocation + { + [JsonProperty("component")] + public string Component { get; set; } - [JsonProperty("msg")] - public string Message { get; set; } - } + [JsonProperty("textRange")] + public ServerIssueTextRange TextRange { get; set; } - #endregion // JSON data classes + [JsonProperty("msg")] + public string Message { get; set; } } + + #endregion // JSON data classes } diff --git a/src/SonarQube.Client/Api/V7_20/GetIssuesRequestWrapper.cs b/src/SonarQube.Client/Api/V7_20/GetIssuesRequestWrapper.cs index 8c44b38718..f142ee65f2 100644 --- a/src/SonarQube.Client/Api/V7_20/GetIssuesRequestWrapper.cs +++ b/src/SonarQube.Client/Api/V7_20/GetIssuesRequestWrapper.cs @@ -18,105 +18,89 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; -using System.Linq; using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using SonarQube.Client.Logging; using SonarQube.Client.Models; -namespace SonarQube.Client.Api.V7_20 +namespace SonarQube.Client.Api.V7_20; + +/// +/// The SonarQube 10k API result limit problem (https://github.com/SonarSource/sonarlint-visualstudio/issues/776): +/// SonarQube will return the first 10k results from any query.The suppressed issues in large +/// projects could be more than 10k and SLVS will not hide those which are not returned by the +/// server. +/// +/// To reduce the effects of this limitation we will retrieve issues in batches by issue type. +/// The same approach is used in the other flavours of SonarLint. +/// +/// This class should be removed if/when SonarQube removes the 10k API result limitation. +/// +internal class GetIssuesRequestWrapper : IGetIssuesRequest + where T : GetIssuesWithComponentRequest, new() { - /// - /// The SonarQube 10k API result limit problem (https://github.com/SonarSource/sonarlint-visualstudio/issues/776): - /// SonarQube will return the first 10k results from any query.The suppressed issues in large - /// projects could be more than 10k and SLVS will not hide those which are not returned by the - /// server. - /// - /// To reduce the effects of this limitation we will retrieve issues in batches by issue type. - /// The same approach is used in the other flavours of SonarLint. - /// - /// This class should be removed if/when SonarQube removes the 10k API result limitation. - /// - internal class GetIssuesRequestWrapper : IGetIssuesRequest - where T : GetIssuesWithComponentRequest, new() - { - private readonly T innerRequest = new T(); + private readonly T innerRequest = new T(); - public string ProjectKey { get; set; } + public string ProjectKey { get; set; } - public string Statuses { get; set; } + public string Statuses { get; set; } - public string Branch { get; set; } + public string Branch { get; set; } - public string[] IssueKeys { get; set; } + public string[] IssueKeys { get; set; } - public string RuleId { get; set; } + public string RuleId { get; set; } - public string ComponentKey { get; set; } - public ILogger Logger { get; set; } - public bool IncludeTaint { get; set; } = true; + public string ComponentKey { get; set; } + public ILogger Logger { get; set; } - public async Task InvokeAsync(HttpClient httpClient, CancellationToken token) + public async Task InvokeAsync(HttpClient httpClient, CancellationToken token) + { + // Transfer all IGetIssuesRequest properties to the inner request. If more properties are + // added to IGetIssuesRequest, this block should set them. + innerRequest.ProjectKey = ProjectKey; + innerRequest.Statuses = Statuses; + innerRequest.Branch = Branch; + innerRequest.Logger = Logger; + innerRequest.IssueKeys = IssueKeys; + innerRequest.RuleId = RuleId; + innerRequest.ComponentKey = ComponentKey; + + if (innerRequest.IssueKeys != null) { - // Transfer all IGetIssuesRequest properties to the inner request. If more properties are - // added to IGetIssuesRequest, this block should set them. - innerRequest.ProjectKey = ProjectKey; - innerRequest.Statuses = Statuses; - innerRequest.Branch = Branch; - innerRequest.Logger = Logger; - innerRequest.IssueKeys = IssueKeys; - innerRequest.RuleId = RuleId; - innerRequest.ComponentKey = ComponentKey; - - if (innerRequest.IssueKeys != null) - { - var response = await innerRequest.InvokeAsync(httpClient, token); - - return response; - } - - ResetInnerRequest(); - innerRequest.Types = "CODE_SMELL"; - var codeSmells = await innerRequest.InvokeAsync(httpClient, token); - WarnForApiLimit(codeSmells, innerRequest, "code smells"); - - ResetInnerRequest(); - innerRequest.Types = "BUG"; - var bugs = await innerRequest.InvokeAsync(httpClient, token); - WarnForApiLimit(bugs, innerRequest, "bugs"); - - var vulnerabilities = Array.Empty(); - if (IncludeTaint) - { - ResetInnerRequest(); - innerRequest.Types = "VULNERABILITY"; - vulnerabilities = await innerRequest.InvokeAsync(httpClient, token); - WarnForApiLimit(vulnerabilities, innerRequest, "vulnerabilities"); - } - return codeSmells - .Concat(bugs) - .Concat(vulnerabilities) - .ToArray(); - } + var response = await innerRequest.InvokeAsync(httpClient, token); - private void WarnForApiLimit(SonarQubeIssue[] issues, GetIssuesRequest request, string friendlyIssueType) - { - if (issues.Length == request.ItemsLimit) - { - Logger.Warning($"Sonar web API response limit reached ({request.ItemsLimit} items). Some {friendlyIssueType} might not be suppressed."); - } + return response; } - /// - /// For paged requests the Page property is automatically changed on each invocation. - /// We are resetting it so that our invocations for different issue types could start - /// from the first page. - /// - private void ResetInnerRequest() + ResetInnerRequest(); + innerRequest.Types = "CODE_SMELL"; + var codeSmells = await innerRequest.InvokeAsync(httpClient, token); + WarnForApiLimit(codeSmells, innerRequest, "code smells"); + + ResetInnerRequest(); + innerRequest.Types = "BUG"; + var bugs = await innerRequest.InvokeAsync(httpClient, token); + WarnForApiLimit(bugs, innerRequest, "bugs"); + return codeSmells + .Concat(bugs) + .ToArray(); + } + + private void WarnForApiLimit(SonarQubeIssue[] issues, GetIssuesRequest request, string friendlyIssueType) + { + if (issues.Length == request.ItemsLimit) { - innerRequest.Page = 1; + Logger.Warning($"Sonar web API response limit reached ({request.ItemsLimit} items). Some {friendlyIssueType} might not be suppressed."); } } + + /// + /// For paged requests the Page property is automatically changed on each invocation. + /// We are resetting it so that our invocations for different issue types could start + /// from the first page. + /// + private void ResetInnerRequest() + { + innerRequest.Page = 1; + } } diff --git a/src/SonarQube.Client/Api/V8_6/GetTaintVulnerabilitiesRequest.cs b/src/SonarQube.Client/Api/V8_6/GetTaintVulnerabilitiesRequest.cs deleted file mode 100644 index 30b0b41fac..0000000000 --- a/src/SonarQube.Client/Api/V8_6/GetTaintVulnerabilitiesRequest.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using SonarQube.Client.Api.V7_20; -using SonarQube.Client.Logging; -using SonarQube.Client.Models; - -namespace SonarQube.Client.Api.V8_6 -{ - internal class GetTaintVulnerabilitiesRequest : IGetTaintVulnerabilitiesRequest - { - protected GetIssuesRequest getIssuesRequest = new GetIssuesRequest(); - - public string ProjectKey { get; set; } - public string Branch { get; set; } - public ILogger Logger { get; set; } - - public virtual async Task InvokeAsync(HttpClient httpClient, CancellationToken token) - { - getIssuesRequest.Logger = Logger; - getIssuesRequest.ProjectKey = ProjectKey; - getIssuesRequest.Branch = Branch; - getIssuesRequest.Statuses = "OPEN,CONFIRMED,REOPENED,RESOLVED"; - getIssuesRequest.Types = "VULNERABILITY"; - - var vulnerabilities = await getIssuesRequest.InvokeAsync(httpClient, token); - WarnForApiLimit(vulnerabilities, getIssuesRequest); - - var taintVulnerabilities = vulnerabilities - .Where(x => x.RuleId.Contains("security")) - .ToArray(); - - return taintVulnerabilities; - } - - private void WarnForApiLimit(SonarQubeIssue[] issues, GetIssuesRequest request) - { - if (issues.Length == request.ItemsLimit) - { - Logger.Warning($"Sonar web API response limit reached ({request.ItemsLimit} items). Some vulnerabilities might not be shown."); - } - } - } -} diff --git a/src/SonarQube.Client/Api/V9_6/GetTaintVulnerabilitiesWithContextRequest.cs b/src/SonarQube.Client/Api/V9_6/GetTaintVulnerabilitiesWithContextRequest.cs deleted file mode 100644 index ebecb3993d..0000000000 --- a/src/SonarQube.Client/Api/V9_6/GetTaintVulnerabilitiesWithContextRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using SonarQube.Client.Api.V8_6; -using SonarQube.Client.Models; - -namespace SonarQube.Client.Api.V9_6 -{ - internal class GetTaintVulnerabilitiesWithContextRequest : GetTaintVulnerabilitiesRequest - { - public override async Task InvokeAsync(HttpClient httpClient, CancellationToken token) - { - getIssuesRequest = new GetIssuesWithContextRequest(); - - return await base.InvokeAsync(httpClient, token); - } - } -} diff --git a/src/SonarQube.Client/ISonarQubeService.cs b/src/SonarQube.Client/ISonarQubeService.cs index 5fcdadf8f5..82156f3993 100644 --- a/src/SonarQube.Client/ISonarQubeService.cs +++ b/src/SonarQube.Client/ISonarQubeService.cs @@ -126,14 +126,6 @@ Task> GetNotificationEventsAsync(string projectKey, Task GetHotspotAsync(string hotspotKey, CancellationToken token); - /// - /// Returns the taint issues for the specified project/branch. - /// - /// The project identifier - /// (optional) The Sonar branch for which taint issues should be returned. If null/empty, - /// the issues for the "main" branch will be returned. - Task> GetTaintVulnerabilitiesAsync(string projectKey, string branch, CancellationToken token); - /// /// Returns the URI to view the specified issue on the server /// diff --git a/src/SonarQube.Client/SonarQubeService.cs b/src/SonarQube.Client/SonarQubeService.cs index 14ce3faed5..35617bf6fd 100644 --- a/src/SonarQube.Client/SonarQubeService.cs +++ b/src/SonarQube.Client/SonarQubeService.cs @@ -343,7 +343,6 @@ public async Task> GetIssuesForComponentAsync(string proje request.Branch = branch; request.ComponentKey = componentKey; request.RuleId = ruleId; - request.IncludeTaint = false; }, token); } @@ -447,19 +446,6 @@ public async Task TransitionIssueAsync(string is : SonarQubeIssueTransitionResult.CommentAdditionFailed; } - public async Task> GetTaintVulnerabilitiesAsync(string projectKey, string branch, CancellationToken token) - { - var issues = await InvokeCheckedRequestAsync( - request => - { - request.ProjectKey = projectKey; - request.Branch = branch; - }, token); - - await secondaryIssueHashUpdater.UpdateHashesAsync(issues, this, token); - return issues; - } - public Uri GetViewIssueUrl(string projectKey, string issueKey) { EnsureIsConnected();