]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12717 use enum for SQ categories in SecurityStandards
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 29 Nov 2019 08:26:51 +0000 (09:26 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 13 Jan 2020 19:46:25 +0000 (20:46 +0100)
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java

index 862c468bf167e24a877a2f4552d4ab217822096e..84670cf12cf03f5a196ab1122c084428a39cd936 100644 (file)
@@ -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;
     }
 
index cf2512fcfeb0739676e8051ae5d00a4fc400cd50..cf5102367f2b89fa480bbca361af8625ef9b9ce0 100644 (file)
@@ -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())
index 9a0e785275484e7e540ef645a7cdc2bff49dd1c7..d24cc68f8de0609ba4d7ce5ba63f3ded4fa67cfc 100644 (file)
  */
 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<String, Set<String>> CWES_BY_SQ_CATEGORY = ImmutableMap.<String, Set<String>>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<SQCategory, Set<String>> CWES_BY_SQ_CATEGORY = ImmutableMap.<SQCategory, Set<String>>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<String> SQ_CATEGORIES = ImmutableSet.<String>builder().addAll(CWES_BY_SQ_CATEGORY.keySet()).add(SQ_OTHER_CATEGORY).build();
-  public static final Ordering<String> SQ_CATEGORIES_ORDERING = Ordering.explicit(
-    ImmutableList.<String>builder().addAll(CWES_BY_SQ_CATEGORY.keySet()).add(SQ_OTHER_CATEGORY).build());
+  public static final Ordering<String> SQ_CATEGORY_KEYS_ORDERING = Ordering.explicit(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).collect(Collectors.toList()));
 
   private final Set<String> standards;
   private final Set<String> cwe;
   private final Set<String> owaspTop10;
   private final Set<String> sansTop25;
-  private final Set<String> sq;
+  private final Set<SQCategory> sq;
 
-  private SecurityStandards(Set<String> standards, Set<String> cwe, Set<String> owaspTop10, Set<String> sansTop25, Set<String> sq) {
+  private SecurityStandards(Set<String> standards, Set<String> cwe, Set<String> owaspTop10, Set<String> sansTop25, Set<SQCategory> sq) {
     this.standards = standards;
     this.cwe = cwe;
     this.owaspTop10 = owaspTop10;
@@ -109,7 +140,7 @@ public final class SecurityStandards {
     return sansTop25;
   }
 
-  public Set<String> getSq() {
+  public Set<SQCategory> getSq() {
     return sq;
   }
 
@@ -120,7 +151,7 @@ public final class SecurityStandards {
     Set<String> owaspTop10 = toOwaspTop10(standards);
     Set<String> cwe = toCwe(standards);
     Set<String> sansTop25 = toSansTop25(cwe);
-    Set<String> sq = toSonarSourceSecurityCategories(cwe);
+    Set<SQCategory> 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<String> toSonarSourceSecurityCategories(Collection<String> cwe) {
-    Set<String> result = CWES_BY_SQ_CATEGORY
+  private static Set<SQCategory> toSQCategories(Collection<String> cwe) {
+    Set<SQCategory> 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;
   }
 }
index c78c86cd4c05295ab272a4d7f2b5a123f9029f51..066e8162610b9889d43e7dfcd225b28f6681beb8 100644 (file)
@@ -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
index 7dba189141a0a3a2f96a4ab1c22631e19c2bee15..58ca0dccbfca2cfb1462553589490ecb0d29d792 100644 (file)
@@ -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<SecurityStandardCategoryStatistics> 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<SecurityStandardCategoryStatistics> 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<SecurityStandardCategoryStatistics> 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<Map<String, Set<String>>> categoryToCwesMap) {
-    return addSecurityReportSubAggregations(AggregationBuilders
-        .filter(category, boolQuery()
-          .filter(termQuery(categoryField, category))),
-      includeCwe, categoryToCwesMap.map(m -> m.get(category)));
-  }
-
   private static List<SecurityStandardCategoryStatistics> 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<Set<String>> cwesInCategory) {
+  private static AggregationBuilder newSecurityReportSubAggregations(AggregationBuilder categoriesAggs, boolean includeCwe, Optional<Set<String>> 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;
   }
index ec3489534511857717d21004e1226024073f20fa..7c7c4746d5d94245aeed321cd5c73f429379c4bd 100644 (file)
@@ -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)
index bd44b41d8f02294bf9b9902f62892c5b855281bf..ff67fbccd167fcfc4e6df9675f7e89d2375c7d1e 100644 (file)
  */
 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