From 2f910c548ed1175cd171950d1bff6f9f87945ed4 Mon Sep 17 00:00:00 2001 From: Matteo Mara Date: Mon, 25 Jul 2022 12:49:59 +0200 Subject: [PATCH] SONAR-17061 add PCI DSS filter to hotspots search API --- .../sonar/server/hotspot/ws/SearchAction.java | 40 +++++++++++++++++-- .../server/hotspot/ws/SearchActionTest.java | 39 ++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java index 2e0ff1b321e..7a6b742820a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java @@ -41,6 +41,7 @@ import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.RuleType; +import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -109,6 +110,8 @@ public class SearchAction implements HotspotsWsAction { private static final String PARAM_PULL_REQUEST = "pullRequest"; private static final String PARAM_IN_NEW_CODE_PERIOD = "inNewCodePeriod"; private static final String PARAM_ONLY_MINE = "onlyMine"; + private static final String PARAM_PCI_DSS_32 = "pciDss-3.2"; + private static final String PARAM_PCI_DSS_40 = "pciDss-4.0"; private static final String PARAM_OWASP_TOP_10_2017 = "owaspTop10"; private static final String PARAM_OWASP_TOP_10_2021 = "owaspTop10-2021"; private static final String PARAM_SANS_TOP_25 = "sansTop25"; @@ -144,6 +147,8 @@ public class SearchAction implements HotspotsWsAction { private static WsRequest toWsRequest(Request request) { Set hotspotKeys = setFromList(request.paramAsStrings(PARAM_HOTSPOTS)); + Set pciDss32 = setFromList(request.paramAsStrings(PARAM_PCI_DSS_32)); + Set pciDss40 = setFromList(request.paramAsStrings(PARAM_PCI_DSS_40)); Set owasp2017Top10 = setFromList(request.paramAsStrings(PARAM_OWASP_TOP_10_2017)); Set owasp2021Top10 = setFromList(request.paramAsStrings(PARAM_OWASP_TOP_10_2021)); Set sansTop25 = setFromList(request.paramAsStrings(PARAM_SANS_TOP_25)); @@ -154,7 +159,7 @@ public class SearchAction implements HotspotsWsAction { return new WsRequest( request.mandatoryParamAsInt(PAGE), request.mandatoryParamAsInt(PAGE_SIZE), request.param(PARAM_PROJECT_KEY), request.param(PARAM_BRANCH), request.param(PARAM_PULL_REQUEST), hotspotKeys, request.param(PARAM_STATUS), request.param(PARAM_RESOLUTION), - request.paramAsBoolean(PARAM_IN_NEW_CODE_PERIOD), request.paramAsBoolean(PARAM_ONLY_MINE), owasp2017Top10, owasp2021Top10, sansTop25, + request.paramAsBoolean(PARAM_IN_NEW_CODE_PERIOD), request.paramAsBoolean(PARAM_ONLY_MINE), pciDss32, pciDss40, owasp2017Top10, owasp2021Top10, sansTop25, sonarsourceSecurity, cwes, files); } @@ -192,7 +197,9 @@ public class SearchAction implements HotspotsWsAction { + "For applications, it also requires 'Browse' permission on its child projects.
" + "When issue indexation is in progress returns 503 service unavailable HTTP code.") .setSince("8.1") - .setInternal(true); + .setInternal(true) + .setChangelog( + new Change("9.6", "Added parameters 'pciDss-3.2' and 'pciDss-4.0")); action.addPagingParams(100); action.createParam(PARAM_PROJECT_KEY) @@ -226,6 +233,14 @@ public class SearchAction implements HotspotsWsAction { .setBooleanPossibleValues() .setDefaultValue("false") .setSince("9.5"); + action.createParam(PARAM_PCI_DSS_32) + .setDescription("Comma-separated list of PCI DSS v3.2 categories.") + .setSince("9.6") + .setExampleValue("4,6.5.8,10.1"); + action.createParam(PARAM_PCI_DSS_40) + .setDescription("Comma-separated list of PCI DSS v4.0 categories.") + .setSince("9.6") + .setExampleValue("4,6.5.8,10.1"); action.createParam(PARAM_ONLY_MINE) .setDescription("If 'projectKey' is provided, returns only Security Hotspots assigned to the current user") .setBooleanPossibleValues() @@ -395,6 +410,12 @@ public class SearchAction implements HotspotsWsAction { } private static void addSecurityStandardFilters(WsRequest wsRequest, IssueQuery.Builder builder) { + if (!wsRequest.getPciDss32().isEmpty()) { + builder.pciDss32(wsRequest.getPciDss32()); + } + if (!wsRequest.getPciDss40().isEmpty()) { + builder.pciDss40(wsRequest.getPciDss40()); + } if (!wsRequest.getOwaspTop10For2017().isEmpty()) { builder.owaspTop10(wsRequest.getOwaspTop10For2017()); } @@ -615,6 +636,8 @@ public class SearchAction implements HotspotsWsAction { private final String resolution; private final boolean inNewCodePeriod; private final boolean onlyMine; + private final Set pciDss32; + private final Set pciDss40; private final Set owaspTop10For2017; private final Set owaspTop10For2021; private final Set sansTop25; @@ -626,7 +649,8 @@ public class SearchAction implements HotspotsWsAction { @Nullable String projectKey, @Nullable String branch, @Nullable String pullRequest, Set hotspotKeys, @Nullable String status, @Nullable String resolution, @Nullable Boolean inNewCodePeriod, - @Nullable Boolean onlyMine, Set owaspTop10For2017, Set owaspTop10For2021, Set sansTop25, Set sonarsourceSecurity, + @Nullable Boolean onlyMine, Set pciDss32, Set pciDss40, Set owaspTop10For2017, Set owaspTop10For2021, Set sansTop25, + Set sonarsourceSecurity, Set cwe, @Nullable Set files) { this.page = page; this.index = index; @@ -638,6 +662,8 @@ public class SearchAction implements HotspotsWsAction { this.resolution = resolution; this.inNewCodePeriod = inNewCodePeriod != null && inNewCodePeriod; this.onlyMine = onlyMine != null && onlyMine; + this.pciDss32 = pciDss32; + this.pciDss40 = pciDss40; this.owaspTop10For2017 = owaspTop10For2017; this.owaspTop10For2021 = owaspTop10For2021; this.sansTop25 = sansTop25; @@ -686,6 +712,14 @@ public class SearchAction implements HotspotsWsAction { return onlyMine; } + public Set getPciDss32() { + return pciDss32; + } + + public Set getPciDss40() { + return pciDss40; + } + public Set getOwaspTop10For2017() { return owaspTop10For2017; } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java index 230d636f669..20c49231e41 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/SearchActionTest.java @@ -121,6 +121,8 @@ public class SearchActionTest { private static final String PARAM_PULL_REQUEST = "pullRequest"; private static final String PARAM_IN_NEW_CODE_PERIOD = "inNewCodePeriod"; private static final String PARAM_ONLY_MINE = "onlyMine"; + private static final String PARAM_PCI_DSS_32 = "pciDss-3.2"; + private static final String PARAM_PCI_DSS_40 = "pciDss-4.0"; private static final String PARAM_OWASP_TOP_10_2017 = "owaspTop10"; private static final String PARAM_OWASP_TOP_10_2021 = "owaspTop10-2021"; private static final String PARAM_SANS_TOP_25 = "sansTop25"; @@ -154,6 +156,8 @@ public class SearchActionTest { @Test public void verify_ws_def() { WebService.Param onlyMineParam = actionTester.getDef().param(PARAM_ONLY_MINE); + WebService.Param pciDss32Param = actionTester.getDef().param(PARAM_PCI_DSS_32); + WebService.Param pciDss40Param = actionTester.getDef().param(PARAM_PCI_DSS_40); WebService.Param owaspTop10Param = actionTester.getDef().param(PARAM_OWASP_TOP_10_2017); WebService.Param sansTop25Param = actionTester.getDef().param(PARAM_SANS_TOP_25); WebService.Param sonarsourceSecurityParam = actionTester.getDef().param(PARAM_SONARSOURCE_SECURITY); @@ -165,6 +169,10 @@ public class SearchActionTest { assertThat(actionTester.getDef().param(PARAM_ONLY_MINE).possibleValues()) .containsExactlyInAnyOrder("yes", "no", "true", "false"); + assertThat(pciDss32Param).isNotNull(); + assertThat(pciDss32Param.isRequired()).isFalse(); + assertThat(pciDss40Param).isNotNull(); + assertThat(pciDss40Param.isRequired()).isFalse(); assertThat(owaspTop10Param).isNotNull(); assertThat(owaspTop10Param.isRequired()).isFalse(); assertThat(sansTop25Param).isNotNull(); @@ -1455,6 +1463,37 @@ public class SearchActionTest { .containsExactly(hotspot3.getKey()); } + @Test + public void returns_hotspots_with_specified_pciDss_category() { + ComponentDto project = dbTester.components().insertPublicProject(); + userSessionRule.registerComponents(project); + indexPermissions(); + ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); + RuleDto rule1 = newRule(SECURITY_HOTSPOT); + RuleDto rule2 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("cwe:117", "cwe:190"))); + RuleDto rule3 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("pciDss-3.2:1.2.3"))); + RuleDto rule4 = newRule(SECURITY_HOTSPOT, r -> r.setSecurityStandards(of("pciDss-4.0:2.3a"))); + insertHotspot(project, file, rule1); + insertHotspot(project, file, rule2); + IssueDto hotspot3 = insertHotspot(project, file, rule3); + IssueDto hotspot4 = insertHotspot(project, file, rule4); + indexIssues(); + + SearchWsResponse response32 = newRequest(project).setParam(PARAM_PCI_DSS_32, "1") + .executeProtobuf(SearchWsResponse.class); + + assertThat(response32.getHotspotsList()) + .extracting(SearchWsResponse.Hotspot::getKey) + .containsExactly(hotspot3.getKey()); + + SearchWsResponse response40 = newRequest(project).setParam(PARAM_PCI_DSS_40, "2") + .executeProtobuf(SearchWsResponse.class); + + assertThat(response40.getHotspotsList()) + .extracting(SearchWsResponse.Hotspot::getKey) + .containsExactly(hotspot4.getKey()); + } + @Test public void returns_hotspots_with_specified_owasp2021Top10_category() { ComponentDto project = dbTester.components().insertPublicProject(); -- 2.39.5