From 3821d2d07b3d299f44a643d77aec3d48fadf6c74 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 29 Nov 2019 09:26:51 +0100 Subject: [PATCH] SONAR-12717 use enum for SQ categories in SecurityStandards --- .../index/IssueIteratorForSingleChunk.java | 4 +- .../org/sonar/server/rule/index/RuleDoc.java | 4 +- .../server/security/SecurityStandards.java | 95 ++++++++++++------- .../server/issue/index/IssueIndexerTest.java | 3 +- .../sonar/server/issue/index/IssueIndex.java | 34 ++++--- .../sonar/server/issue/ws/SearchAction.java | 8 +- .../sonar/server/rule/ws/RuleWsSupport.java | 8 +- 7 files changed, 99 insertions(+), 57 deletions(-) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java index 862c468bf16..84670cf12cf 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java @@ -39,8 +39,10 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.ResultSetIterator; import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.stream.Collectors.toList; import static org.sonar.api.utils.DateUtils.longToDate; import static org.sonar.db.DatabaseUtils.getLong; import static org.sonar.db.rule.RuleDefinitionDto.deserializeSecurityStandardsString; @@ -233,7 +235,7 @@ class IssueIteratorForSingleChunk implements IssueIterator { doc.setOwaspTop10(securityStandards.getOwaspTop10()); doc.setCwe(securityStandards.getCwe()); doc.setSansTop25(securityStandards.getSansTop25()); - doc.setSonarSourceSecurityCategories(securityStandards.getSq()); + doc.setSonarSourceSecurityCategories(securityStandards.getSq().stream().map(SQCategory::getKey).collect(toList())); return doc; } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java index cf2512fcfeb..cf5102367f2 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java @@ -36,7 +36,9 @@ import org.sonar.db.rule.RuleForIndexingDto; import org.sonar.markdown.Markdown; import org.sonar.server.es.BaseDoc; import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; +import static java.util.stream.Collectors.toList; import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE; @@ -280,7 +282,7 @@ public class RuleDoc extends BaseDoc { .setCwe(securityStandards.getCwe()) .setOwaspTop10(securityStandards.getOwaspTop10()) .setSansTop25(securityStandards.getSansTop25()) - .setSonarSourceSecurityCategories(securityStandards.getSq()) + .setSonarSourceSecurityCategories(securityStandards.getSq().stream().map(SQCategory::getKey).collect(toList())) .setName(dto.getName()) .setRuleKey(dto.getPluginRuleKey()) .setSeverity(dto.getSeverityAsString()) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java index 9a0e7852754..d24cc68f8de 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java @@ -19,15 +19,16 @@ */ package org.sonar.server.security; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Ordering; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.concurrent.Immutable; import org.sonar.core.util.stream.MoreCollectors; @@ -53,39 +54,69 @@ public final class SecurityStandards { SANS_TOP_25_RISKY_RESOURCE, RISKY_CWE, SANS_TOP_25_POROUS_DEFENSES, POROUS_CWE); - public static final Map> CWES_BY_SQ_CATEGORY = ImmutableMap.>builder() - .put("sql-injection", ImmutableSet.of("89", "564")) - .put("command-injection", ImmutableSet.of("77", "78", "88", "214")) - .put("path-traversal-injection", ImmutableSet.of("22")) - .put("ldap-injection", ImmutableSet.of("90")) - .put("xpath-injection", ImmutableSet.of("643")) - .put("rce", ImmutableSet.of("94", "95")) - .put("dos", ImmutableSet.of("400", "624")) - .put("ssrf", ImmutableSet.of("918")) - .put("csrf", ImmutableSet.of("352")) - .put("xss", ImmutableSet.of("79", "80", "81", "82", "83", "84", "85", "86", "87")) - .put("log-injection", ImmutableSet.of("117")) - .put("http-response-splitting", ImmutableSet.of("113")) - .put("open-redirect", ImmutableSet.of("601")) - .put("xxe", ImmutableSet.of("611", "827")) - .put("object-injection", ImmutableSet.of("134", "470", "502")) - .put("weak-cryptography", ImmutableSet.of("295", "297", "321", "322", "323", "324", "325", "326", "327", "328", "330", "780")) - .put("auth", ImmutableSet.of("798", "640", "620", "549", "522", "521", "263", "262", "261", "259", "284")) - .put("insecure-conf", ImmutableSet.of("102", "215", "311", "315", "346", "614", "489", "942")) - .put("file-manipulation", ImmutableSet.of("97", "73")) + public enum SQCategory { + SQL_INJECTION("sql-injection"), + COMMAND_INJECTION("command-injection"), + PATH_TRAVERSAL_INJECTION("path-traversal-injection"), + LDAP_INJECTION("ldap-injection"), + XPATH_INJECT("xpath-injection"), + RCE("rce"), + DOS("dos"), + SSRF("ssrf"), + CSRF("csrf"), + XSS("xss"), + LOG_INJECTION("log-injection"), + HTTP_RESPONSE_SPLITTING("http-response-splitting"), + OPEN_REDIRECT("open-redirect"), + XXE("xxe"), + OBJECT_INJECTION("object-injection"), + WEAK_CRYPTOGRAPHY("weak-cryptography"), + AUTH("auth"), + INSECURE_CONF("insecure-conf"), + FILE_MANIPULATION("file-manipulation"), + OTHERS("others"); + + private final String key; + + SQCategory(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + } + + public static final Map> CWES_BY_SQ_CATEGORY = ImmutableMap.>builder() + .put(SQCategory.SQL_INJECTION, ImmutableSet.of("89", "564")) + .put(SQCategory.COMMAND_INJECTION, ImmutableSet.of("77", "78", "88", "214")) + .put(SQCategory.PATH_TRAVERSAL_INJECTION, ImmutableSet.of("22")) + .put(SQCategory.LDAP_INJECTION, ImmutableSet.of("90")) + .put(SQCategory.XPATH_INJECT, ImmutableSet.of("643")) + .put(SQCategory.RCE, ImmutableSet.of("94", "95")) + .put(SQCategory.DOS, ImmutableSet.of("400", "624")) + .put(SQCategory.SSRF, ImmutableSet.of("918")) + .put(SQCategory.CSRF, ImmutableSet.of("352")) + .put(SQCategory.XSS, ImmutableSet.of("79", "80", "81", "82", "83", "84", "85", "86", "87")) + .put(SQCategory.LOG_INJECTION, ImmutableSet.of("117")) + .put(SQCategory.HTTP_RESPONSE_SPLITTING, ImmutableSet.of("113")) + .put(SQCategory.OPEN_REDIRECT, ImmutableSet.of("601")) + .put(SQCategory.XXE, ImmutableSet.of("611", "827")) + .put(SQCategory.OBJECT_INJECTION, ImmutableSet.of("134", "470", "502")) + .put(SQCategory.WEAK_CRYPTOGRAPHY, ImmutableSet.of("295", "297", "321", "322", "323", "324", "325", "326", "327", "328", "330", "780")) + .put(SQCategory.AUTH, ImmutableSet.of("798", "640", "620", "549", "522", "521", "263", "262", "261", "259", "284")) + .put(SQCategory.INSECURE_CONF, ImmutableSet.of("102", "215", "311", "315", "346", "614", "489", "942")) + .put(SQCategory.FILE_MANIPULATION, ImmutableSet.of("97", "73")) .build(); - public static final String SQ_OTHER_CATEGORY = "others"; - public static final Set SQ_CATEGORIES = ImmutableSet.builder().addAll(CWES_BY_SQ_CATEGORY.keySet()).add(SQ_OTHER_CATEGORY).build(); - public static final Ordering SQ_CATEGORIES_ORDERING = Ordering.explicit( - ImmutableList.builder().addAll(CWES_BY_SQ_CATEGORY.keySet()).add(SQ_OTHER_CATEGORY).build()); + public static final Ordering SQ_CATEGORY_KEYS_ORDERING = Ordering.explicit(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).collect(Collectors.toList())); private final Set standards; private final Set cwe; private final Set owaspTop10; private final Set sansTop25; - private final Set sq; + private final Set sq; - private SecurityStandards(Set standards, Set cwe, Set owaspTop10, Set sansTop25, Set sq) { + private SecurityStandards(Set standards, Set cwe, Set owaspTop10, Set sansTop25, Set sq) { this.standards = standards; this.cwe = cwe; this.owaspTop10 = owaspTop10; @@ -109,7 +140,7 @@ public final class SecurityStandards { return sansTop25; } - public Set getSq() { + public Set getSq() { return sq; } @@ -120,7 +151,7 @@ public final class SecurityStandards { Set owaspTop10 = toOwaspTop10(standards); Set cwe = toCwe(standards); Set sansTop25 = toSansTop25(cwe); - Set sq = toSonarSourceSecurityCategories(cwe); + Set sq = toSQCategories(cwe); return new SecurityStandards(standards, cwe, owaspTop10, sansTop25, sq); } @@ -147,12 +178,12 @@ public final class SecurityStandards { .collect(MoreCollectors.toSet()); } - private static Set toSonarSourceSecurityCategories(Collection cwe) { - Set result = CWES_BY_SQ_CATEGORY + private static Set toSQCategories(Collection cwe) { + Set result = CWES_BY_SQ_CATEGORY .keySet() .stream() .filter(k -> cwe.stream().anyMatch(CWES_BY_SQ_CATEGORY.get(k)::contains)) .collect(MoreCollectors.toSet()); - return result.isEmpty() ? singleton(SQ_OTHER_CATEGORY) : result; + return result.isEmpty() ? singleton(SQCategory.OTHERS) : result; } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java index c78c86cd4c0..066e8162610 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java @@ -49,6 +49,7 @@ import org.sonar.server.es.ProjectIndexer; import org.sonar.server.permission.index.AuthorizationScope; import org.sonar.server.permission.index.IndexPermissions; import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -138,7 +139,7 @@ public class IssueIndexerTest { assertThat(doc.getCwe()).containsExactlyInAnyOrder(SecurityStandards.UNKNOWN_STANDARD); assertThat(doc.getOwaspTop10()).isEmpty(); assertThat(doc.getSansTop25()).isEmpty(); - assertThat(doc.getSonarSourceSecurityCategories()).containsOnly(SecurityStandards.SQ_OTHER_CATEGORY); + assertThat(doc.getSonarSourceSecurityCategories()).containsOnly(SQCategory.OTHERS.getKey()); } @Test diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 7dba189141a..58ca0dccbfc 100644 --- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -22,6 +22,7 @@ package org.sonar.server.issue.index; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -83,6 +84,7 @@ import org.sonar.server.issue.index.IssueQuery.PeriodStart; import org.sonar.server.permission.index.AuthorizationDoc; import org.sonar.server.permission.index.WebAuthorizationTypeSupport; import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; import org.sonar.server.user.UserSession; import org.sonar.server.view.index.ViewIndexDefinition; @@ -156,7 +158,6 @@ import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE; -import static org.sonar.server.security.SecurityStandards.SQ_CATEGORIES; import static org.sonar.server.view.index.ViewIndexDefinition.TYPE_VIEW; import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_PARAM_AUTHORS; import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT; @@ -873,31 +874,35 @@ public class IssueIndex { public List getSansTop25Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) { SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp); Stream.of(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE, SANS_TOP_25_POROUS_DEFENSES) - .forEach(sansCategory -> request.addAggregation(createAggregation(FIELD_ISSUE_SANS_TOP_25, sansCategory, includeCwe, Optional.of(SecurityStandards.CWES_BY_SANS_TOP_25)))); + .forEach(sansCategory -> request.addAggregation(newSecurityReportSubAggregations( + AggregationBuilders.filter(sansCategory, boolQuery().filter(termQuery(FIELD_ISSUE_SANS_TOP_25, sansCategory))), + includeCwe, + Optional.ofNullable(SecurityStandards.CWES_BY_SANS_TOP_25.get(sansCategory))))); return processSecurityReportSearchResults(request, includeCwe); } public List getSonarSourceReport(String projectUuid, boolean isViewOrApp, boolean includeCwe) { SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp); - SQ_CATEGORIES.forEach(sonarsourceCategory -> request.addAggregation( - createAggregation(FIELD_ISSUE_SONARSOURCE_SECURITY, sonarsourceCategory, includeCwe, Optional.of(SecurityStandards.CWES_BY_SQ_CATEGORY)))); + Arrays.stream(SQCategory.values()) + .forEach(sonarsourceCategory -> request.addAggregation( + newSecurityReportSubAggregations( + AggregationBuilders.filter(sonarsourceCategory.getKey(), boolQuery().filter(termQuery(FIELD_ISSUE_SONARSOURCE_SECURITY, sonarsourceCategory.getKey()))), + includeCwe, + Optional.ofNullable(SecurityStandards.CWES_BY_SQ_CATEGORY.get(sonarsourceCategory))))); return processSecurityReportSearchResults(request, includeCwe); } public List getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) { SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp); IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i) - .forEach(owaspCategory -> request.addAggregation(createAggregation(FIELD_ISSUE_OWASP_TOP_10, owaspCategory, includeCwe, Optional.empty()))); + .forEach(owaspCategory -> request.addAggregation( + newSecurityReportSubAggregations( + AggregationBuilders.filter(owaspCategory, boolQuery().filter(termQuery(FIELD_ISSUE_OWASP_TOP_10, owaspCategory))), + includeCwe, + Optional.empty()))); return processSecurityReportSearchResults(request, includeCwe); } - private static AggregationBuilder createAggregation(String categoryField, String category, boolean includeCwe, Optional>> categoryToCwesMap) { - return addSecurityReportSubAggregations(AggregationBuilders - .filter(category, boolQuery() - .filter(termQuery(categoryField, category))), - includeCwe, categoryToCwesMap.map(m -> m.get(category))); - } - private static List processSecurityReportSearchResults(SearchRequestBuilder request, boolean includeCwe) { SearchResponse response = request.get(); return response.getAggregations().asList().stream() @@ -935,7 +940,7 @@ public class IssueIndex { reviewedSecurityHotspots, children); } - private static AggregationBuilder addSecurityReportSubAggregations(AggregationBuilder categoriesAggs, boolean includeCwe, Optional> cwesInCategory) { + private static AggregationBuilder newSecurityReportSubAggregations(AggregationBuilder categoriesAggs, boolean includeCwe, Optional> cwesInCategory) { AggregationBuilder aggregationBuilder = addSecurityReportIssueCountAggregations(categoriesAggs); if (includeCwe) { final TermsAggregationBuilder cwesAgg = AggregationBuilders.terms(AGG_CWES) @@ -945,8 +950,7 @@ public class IssueIndex { cwesInCategory.ifPresent(set -> { cwesAgg.includeExclude(new IncludeExclude(set.toArray(new String[0]), new String[0])); }); - categoriesAggs - .subAggregation(addSecurityReportIssueCountAggregations(cwesAgg)); + categoriesAggs.subAggregation(addSecurityReportIssueCountAggregations(cwesAgg)); } return aggregationBuilder; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java index ec348953451..7c7c4746d5d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.SearchHit; @@ -54,7 +55,7 @@ import org.sonar.server.issue.SearchRequest; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.issue.index.IssueQuery; import org.sonar.server.issue.index.IssueQueryFactory; -import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Issues.SearchWsResponse; @@ -88,7 +89,6 @@ import static org.sonar.server.issue.index.IssueQueryFactory.UNKNOWN; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES; import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE; -import static org.sonar.server.security.SecurityStandards.SQ_OTHER_CATEGORY; import static org.sonar.server.security.SecurityStandards.UNKNOWN_STANDARD; import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -273,10 +273,10 @@ public class SearchAction implements IssuesWsAction, Startable { .setDescription("Comma-separated list of CWE identifiers. Use '" + UNKNOWN_STANDARD + "' to select issues not associated to any CWE.") .setExampleValue("12,125," + UNKNOWN_STANDARD); action.createParam(PARAM_SONARSOURCE_SECURITY) - .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQ_OTHER_CATEGORY + "' to select issues not associated" + + .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQCategory.OTHERS.getKey() + "' to select issues not associated" + " with any category") .setSince("7.8") - .setPossibleValues(SecurityStandards.SQ_CATEGORIES); + .setPossibleValues(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).collect(Collectors.toList())); action.createParam(DEPRECATED_PARAM_AUTHORS) .setDeprecatedSince("7.7") .setDescription("This parameter is deprecated, please use '%s' instead", PARAM_AUTHOR) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java index bd44b41d8f0..ff67fbccd16 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java @@ -19,11 +19,13 @@ */ package org.sonar.server.rule.ws; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; @@ -39,6 +41,7 @@ import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.qualityprofile.ActiveRuleInheritance; import org.sonar.server.rule.index.RuleIndexDefinition; import org.sonar.server.security.SecurityStandards; +import org.sonar.server.security.SecurityStandards.SQCategory; import org.sonar.server.user.UserSession; import static org.sonar.api.server.ws.WebService.Param.ASCENDING; @@ -72,7 +75,6 @@ import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_STATUSES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TAGS; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TEMPLATE_KEY; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TYPES; -import static org.sonar.server.security.SecurityStandards.SQ_OTHER_CATEGORY; @ServerSide public class RuleWsSupport { @@ -153,10 +155,10 @@ public class RuleWsSupport { action .createParam(PARAM_SONARSOURCE_SECURITY) - .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQ_OTHER_CATEGORY + "' to select rules not associated" + + .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQCategory.OTHERS.getKey() + "' to select rules not associated" + " with any category") .setSince("7.8") - .setPossibleValues(SecurityStandards.SQ_CATEGORIES) + .setPossibleValues(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).collect(Collectors.toList())) .setExampleValue("sql-injection,command-injection,others"); action -- 2.39.5