]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12717 refactor SecurityStandardHelper into SecurityStandards
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 28 Nov 2019 17:20:26 +0000 (18:20 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 13 Jan 2020 19:46:25 +0000 (20:46 +0100)
and cleanup responsibility for parsing serialized tags and security standards into table RULES

14 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java
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/rule/index/RuleExtensionDoc.java
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandardHelper.java [deleted file]
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java [new file with mode: 0644]
server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java
server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexSecurityReportsTest.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 608eeb9a72f24c25e11c6c6c72b35174e8a707c4..b16251f126480fc43d9a36037acc17acc44f7fda 100644 (file)
  */
 package org.sonar.db.rule;
 
-import java.util.Arrays;
-import java.util.HashSet;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
 import java.util.Objects;
 import java.util.Set;
-import java.util.TreeSet;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.StringUtils;
@@ -37,6 +36,8 @@ import static com.google.common.base.Preconditions.checkArgument;
 
 public class RuleDefinitionDto {
 
+  private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
+
   private Integer id;
   private String repositoryKey;
   private String ruleKey;
@@ -91,6 +92,26 @@ public class RuleDefinitionDto {
   private long createdAt;
   private long updatedAt;
 
+  public static Set<String> deserializeTagsString(@Nullable String tags) {
+    return deserializeStringSet(tags);
+  }
+
+  public static Set<String> deserializeSecurityStandardsString(@Nullable String securityStandards) {
+    return deserializeStringSet(securityStandards);
+  }
+
+  private static Set<String> deserializeStringSet(@Nullable String securityStandards) {
+    if (securityStandards == null || securityStandards.isEmpty()) {
+      return ImmutableSet.of();
+    }
+
+    return ImmutableSet.copyOf(SPLITTER.split(securityStandards));
+  }
+
+  private static String serializeStringSet(@Nullable Set<String> strings) {
+    return strings == null || strings.isEmpty() ? null : StringUtils.join(strings, ',');
+  }
+
   public RuleKey getKey() {
     if (key == null) {
       key = RuleKey.of(getRepositoryKey(), getRuleKey());
@@ -300,7 +321,7 @@ public class RuleDefinitionDto {
   }
 
   public Set<String> getSystemTags() {
-    return systemTags == null ? new HashSet<>() : new TreeSet<>(Arrays.asList(StringUtils.split(systemTags, ',')));
+    return deserializeTagsString(systemTags);
   }
 
   private String getSystemTagsField() {
@@ -312,12 +333,12 @@ public class RuleDefinitionDto {
   }
 
   public RuleDefinitionDto setSystemTags(Set<String> tags) {
-    this.systemTags = tags.isEmpty() ? null : StringUtils.join(tags, ',');
+    this.systemTags = serializeStringSet(tags);
     return this;
   }
 
   public Set<String> getSecurityStandards() {
-    return securityStandards == null ? new HashSet<>() : new TreeSet<>(Arrays.asList(StringUtils.split(securityStandards, ',')));
+    return deserializeSecurityStandardsString(securityStandards);
   }
 
   private String getSecurityStandardsField() {
@@ -329,7 +350,7 @@ public class RuleDefinitionDto {
   }
 
   public RuleDefinitionDto setSecurityStandards(Set<String> standards) {
-    this.securityStandards = standards.isEmpty() ? null : StringUtils.join(standards, ',');
+    this.securityStandards = serializeStringSet(standards);
     return this;
   }
 
index add7bd75d32516b97258bce803f6ce8ebd297036..5a427e86aaa24113af68df896d7098c9b71a77f4 100644 (file)
@@ -19,8 +19,6 @@
  */
 package org.sonar.db.rule;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
@@ -49,9 +47,6 @@ public class RuleForIndexingDto {
   private long createdAt;
   private long updatedAt;
 
-  private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
-  private static final Splitter SECURITY_STANDARDS_SPLITTER = TAGS_SPLITTER;
-
   public Integer getId() {
     return id;
   }
@@ -88,12 +83,12 @@ public class RuleForIndexingDto {
     return isTemplate;
   }
 
-  public String getSystemTags() {
-    return systemTags;
+  public Set<String> getSystemTags() {
+    return RuleDefinitionDto.deserializeTagsString(systemTags);
   }
 
-  public String getSecurityStandards() {
-    return securityStandards;
+  public Set<String> getSecurityStandards() {
+    return RuleDefinitionDto.deserializeSecurityStandardsString(securityStandards);
   }
 
   public String getTemplateRuleKey() {
@@ -140,12 +135,4 @@ public class RuleForIndexingDto {
   public RuleKey getRuleKey() {
     return RuleKey.of(repository, pluginRuleKey);
   }
-
-  public Set<String> getSystemTagsAsSet() {
-    return ImmutableSet.copyOf(TAGS_SPLITTER.split(systemTags == null ? "" : systemTags));
-  }
-
-  public Set<String> getSecurityStandardsAsSet() {
-    return ImmutableSet.copyOf(SECURITY_STANDARDS_SPLITTER.split(securityStandards == null ? "" : securityStandards));
-  }
 }
index da49894aecd8b52257af3030b466eaec72b34cac..5fc68e9ab2d2024190a4897ce8206e3b09b399d2 100644 (file)
@@ -957,8 +957,8 @@ public class RuleDaoTest {
     assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus());
     assertThat(firstRule.isExternal()).isFalse();
     assertThat(firstRule.isTemplate()).isEqualTo(r1.isTemplate());
-    assertThat(firstRule.getSystemTagsAsSet()).isEqualTo(r1.getSystemTags());
-    assertThat(firstRule.getSecurityStandardsAsSet()).isEqualTo(r1.getSecurityStandards());
+    assertThat(firstRule.getSystemTags()).isEqualTo(r1.getSystemTags());
+    assertThat(firstRule.getSecurityStandards()).isEqualTo(r1.getSecurityStandards());
     assertThat(firstRule.getTemplateRuleKey()).isNull();
     assertThat(firstRule.getTemplateRepository()).isNull();
     assertThat(firstRule.getInternalKey()).isEqualTo(r1.getConfigKey());
@@ -1036,8 +1036,8 @@ public class RuleDaoTest {
     assertThat(firstRule.getSeverityAsString()).isEqualTo(SeverityUtil.getSeverityFromOrdinal(r1.getSeverity()));
     assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus());
     assertThat(firstRule.isTemplate()).isEqualTo(r1.isTemplate());
-    assertThat(firstRule.getSystemTagsAsSet()).isEqualTo(r1.getSystemTags());
-    assertThat(firstRule.getSecurityStandardsAsSet()).isEqualTo(r1.getSecurityStandards());
+    assertThat(firstRule.getSystemTags()).isEqualTo(r1.getSystemTags());
+    assertThat(firstRule.getSecurityStandards()).isEqualTo(r1.getSecurityStandards());
     assertThat(firstRule.getInternalKey()).isEqualTo(r1.getConfigKey());
     assertThat(firstRule.getLanguage()).isEqualTo(r1.getLanguage());
     assertThat(firstRule.getType()).isEqualTo(r1.getType());
index 160247904de494c1984989ee26deefeb87e746f7..862c468bf167e24a877a2f4552d4ab217822096e 100644 (file)
@@ -27,7 +27,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import javax.annotation.CheckForNull;
@@ -39,15 +38,13 @@ import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.ResultSetIterator;
+import org.sonar.server.security.SecurityStandards;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.sonar.api.utils.DateUtils.longToDate;
 import static org.sonar.db.DatabaseUtils.getLong;
-import static org.sonar.server.security.SecurityStandardHelper.getCwe;
-import static org.sonar.server.security.SecurityStandardHelper.getOwaspTop10;
-import static org.sonar.server.security.SecurityStandardHelper.getSansTop25;
-import static org.sonar.server.security.SecurityStandardHelper.getSecurityStandards;
-import static org.sonar.server.security.SecurityStandardHelper.getSonarSourceSecurityCategories;
+import static org.sonar.db.rule.RuleDefinitionDto.deserializeSecurityStandardsString;
+import static org.sonar.server.security.SecurityStandards.fromSecurityStandards;
 
 /**
  * Scrolls over table ISSUES and reads documents to populate
@@ -231,14 +228,12 @@ class IssueIteratorForSingleChunk implements IssueIterator {
       String tags = rs.getString(21);
       doc.setTags(IssueIteratorForSingleChunk.TAGS_SPLITTER.splitToList(tags == null ? "" : tags));
       doc.setType(RuleType.valueOf(rs.getInt(22)));
-      String securityStandards = rs.getString(23);
 
-      List<String> standards = getSecurityStandards(securityStandards);
-      doc.setOwaspTop10(getOwaspTop10(standards));
-      List<String> cwe = getCwe(standards);
-      doc.setCwe(cwe);
-      doc.setSansTop25(getSansTop25(cwe));
-      doc.setSonarSourceSecurityCategories(getSonarSourceSecurityCategories(cwe));
+      SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(rs.getString(23)));
+      doc.setOwaspTop10(securityStandards.getOwaspTop10());
+      doc.setCwe(securityStandards.getCwe());
+      doc.setSansTop25(securityStandards.getSansTop25());
+      doc.setSonarSourceSecurityCategories(securityStandards.getSq());
       return doc;
     }
 
index ff0c4a3a59a042fe605f05c3612339ef6356c70f..cf2512fcfeb0739676e8051ae5d00a4fc400cd50 100644 (file)
@@ -35,7 +35,7 @@ import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleForIndexingDto;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.es.BaseDoc;
-import org.sonar.server.security.SecurityStandardHelper;
+import org.sonar.server.security.SecurityStandards;
 
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
 
@@ -268,7 +268,7 @@ public class RuleDoc extends BaseDoc {
   }
 
   public static RuleDoc of(RuleForIndexingDto dto) {
-    Collection<String> cwe = SecurityStandardHelper.getCwe(dto.getSecurityStandardsAsSet());
+    SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(dto.getSecurityStandards());
     RuleDoc ruleDoc = new RuleDoc()
       .setId(dto.getId())
       .setKey(dto.getRuleKey().toString())
@@ -277,10 +277,10 @@ public class RuleDoc extends BaseDoc {
       .setIsTemplate(dto.isTemplate())
       .setIsExternal(dto.isExternal())
       .setLanguage(dto.getLanguage())
-      .setCwe(cwe)
-      .setOwaspTop10(SecurityStandardHelper.getOwaspTop10(dto.getSecurityStandardsAsSet()))
-      .setSansTop25(SecurityStandardHelper.getSansTop25(cwe))
-      .setSonarSourceSecurityCategories(SecurityStandardHelper.getSonarSourceSecurityCategories(cwe))
+      .setCwe(securityStandards.getCwe())
+      .setOwaspTop10(securityStandards.getOwaspTop10())
+      .setSansTop25(securityStandards.getSansTop25())
+      .setSonarSourceSecurityCategories(securityStandards.getSq())
       .setName(dto.getName())
       .setRuleKey(dto.getPluginRuleKey())
       .setSeverity(dto.getSeverityAsString())
index b6982841a4fed4c4a5820578c2a23d2e034b796d..8894f8f15e2c1c8dc43c78157d70036bb075ea56 100644 (file)
@@ -81,7 +81,7 @@ public class RuleExtensionDoc extends BaseDoc {
     return new RuleExtensionDoc()
       .setRuleId(rule.getId())
       .setScope(RuleExtensionScope.system())
-      .setTags(rule.getSystemTagsAsSet());
+      .setTags(rule.getSystemTags());
   }
 
   public static RuleExtensionDoc of(RuleExtensionForIndexingDto rule) {
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandardHelper.java b/server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandardHelper.java
deleted file mode 100644 (file)
index acfbb9f..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.
- */
-package org.sonar.server.security;
-
-import com.google.common.base.Splitter;
-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.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.Nullable;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-import static java.util.stream.Collectors.toList;
-
-public class SecurityStandardHelper {
-
-  public static final String UNKNOWN_STANDARD = "unknown";
-  public static final String SANS_TOP_25_INSECURE_INTERACTION = "insecure-interaction";
-  public static final String SANS_TOP_25_RISKY_RESOURCE = "risky-resource";
-  public static final String SANS_TOP_25_POROUS_DEFENSES = "porous-defenses";
-
-  private static final String OWASP_TOP10_PREFIX = "owaspTop10:";
-  private static final String CWE_PREFIX = "cwe:";
-  // See https://www.sans.org/top25-software-errors
-  private static final Set<String> INSECURE_CWE = new HashSet<>(asList("89", "78", "79", "434", "352", "601"));
-  private static final Set<String> RISKY_CWE = new HashSet<>(asList("120", "22", "494", "829", "676", "131", "134", "190"));
-  private static final Set<String> POROUS_CWE = new HashSet<>(asList("306", "862", "798", "311", "807", "250", "863", "732", "327", "307", "759"));
-  public static final Map<String, Set<String>> SANS_TOP_25_CWE_MAPPING = ImmutableMap.of(
-    SANS_TOP_25_INSECURE_INTERACTION, INSECURE_CWE,
-    SANS_TOP_25_RISKY_RESOURCE, RISKY_CWE,
-    SANS_TOP_25_POROUS_DEFENSES, POROUS_CWE);
-
-  public static final Map<String, Set<String>> SONARSOURCE_CWE_MAPPING = 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"))
-    .build();
-  public static final String SONARSOURCE_OTHER_CWES_CATEGORY = "others";
-  public static final Ordering<String> SONARSOURCE_CATEGORY_ORDERING = Ordering.explicit(
-    ImmutableList.<String>builder().addAll(SONARSOURCE_CWE_MAPPING.keySet()).add(SONARSOURCE_OTHER_CWES_CATEGORY).build());
-
-  private static final Splitter SECURITY_STANDARDS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
-
-  private SecurityStandardHelper() {
-    // Utility class
-  }
-
-  public static List<String> getSecurityStandards(@Nullable String securityStandards) {
-    return securityStandards == null ? emptyList() : SECURITY_STANDARDS_SPLITTER.splitToList(securityStandards);
-  }
-
-  public static List<String> getSansTop25(Collection<String> cwe) {
-    return SANS_TOP_25_CWE_MAPPING
-      .keySet()
-      .stream()
-      .filter(k -> cwe.stream().anyMatch(SANS_TOP_25_CWE_MAPPING.get(k)::contains))
-      .collect(toList());
-  }
-
-  public static List<String> getSonarSourceSecurityCategories(Collection<String> cwe) {
-    List<String> result = SONARSOURCE_CWE_MAPPING
-      .keySet()
-      .stream()
-      .filter(k -> cwe.stream().anyMatch(SONARSOURCE_CWE_MAPPING.get(k)::contains))
-      .collect(toList());
-    return result.isEmpty() ? singletonList(SONARSOURCE_OTHER_CWES_CATEGORY) : result;
-  }
-
-  public static List<String> getOwaspTop10(Collection<String> securityStandards) {
-    return securityStandards.stream()
-      .filter(s -> s.startsWith(OWASP_TOP10_PREFIX))
-      .map(s -> s.substring(OWASP_TOP10_PREFIX.length()))
-      .collect(toList());
-  }
-
-  public static List<String> getCwe(Collection<String> securityStandards) {
-    List<String> result = securityStandards.stream()
-      .filter(s -> s.startsWith(CWE_PREFIX))
-      .map(s -> s.substring(CWE_PREFIX.length()))
-      .collect(toList());
-    return result.isEmpty() ? singletonList(UNKNOWN_STANDARD) : result;
-  }
-
-  public static List<String> getSansTop25(String securityStandards) {
-    return getSansTop25(getCwe(getSecurityStandards(securityStandards)));
-  }
-
-  public static List<String> getSonarSourceSecurityCategories(String securityStandards) {
-    return getSonarSourceSecurityCategories(getCwe(getSecurityStandards(securityStandards)));
-  }
-
-  public static List<String> getOwaspTop10(String securityStandards) {
-    return getOwaspTop10(getSecurityStandards(securityStandards));
-  }
-
-  public static List<String> getCwe(String securityStandards) {
-    return getCwe(getSecurityStandards(securityStandards));
-  }
-}
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
new file mode 100644 (file)
index 0000000..9a0e785
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.
+ */
+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.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.core.util.stream.MoreCollectors;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singleton;
+
+@Immutable
+public final class SecurityStandards {
+
+  public static final String UNKNOWN_STANDARD = "unknown";
+  public static final String SANS_TOP_25_INSECURE_INTERACTION = "insecure-interaction";
+  public static final String SANS_TOP_25_RISKY_RESOURCE = "risky-resource";
+  public static final String SANS_TOP_25_POROUS_DEFENSES = "porous-defenses";
+
+  private static final String OWASP_TOP10_PREFIX = "owaspTop10:";
+  private static final String CWE_PREFIX = "cwe:";
+  // See https://www.sans.org/top25-software-errors
+  private static final Set<String> INSECURE_CWE = new HashSet<>(asList("89", "78", "79", "434", "352", "601"));
+  private static final Set<String> RISKY_CWE = new HashSet<>(asList("120", "22", "494", "829", "676", "131", "134", "190"));
+  private static final Set<String> POROUS_CWE = new HashSet<>(asList("306", "862", "798", "311", "807", "250", "863", "732", "327", "307", "759"));
+  public static final Map<String, Set<String>> CWES_BY_SANS_TOP_25 = ImmutableMap.of(
+    SANS_TOP_25_INSECURE_INTERACTION, INSECURE_CWE,
+    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"))
+    .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());
+
+  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 SecurityStandards(Set<String> standards, Set<String> cwe, Set<String> owaspTop10, Set<String> sansTop25, Set<String> sq) {
+    this.standards = standards;
+    this.cwe = cwe;
+    this.owaspTop10 = owaspTop10;
+    this.sansTop25 = sansTop25;
+    this.sq = sq;
+  }
+
+  public Set<String> getStandards() {
+    return standards;
+  }
+
+  public Set<String> getCwe() {
+    return cwe;
+  }
+
+  public Set<String> getOwaspTop10() {
+    return owaspTop10;
+  }
+
+  public Set<String> getSansTop25() {
+    return sansTop25;
+  }
+
+  public Set<String> getSq() {
+    return sq;
+  }
+
+  public static SecurityStandards fromSecurityStandards(Set<String> securityStandards) {
+    Set<String> standards = securityStandards.stream()
+      .filter(Objects::nonNull)
+      .collect(MoreCollectors.toSet());
+    Set<String> owaspTop10 = toOwaspTop10(standards);
+    Set<String> cwe = toCwe(standards);
+    Set<String> sansTop25 = toSansTop25(cwe);
+    Set<String> sq = toSonarSourceSecurityCategories(cwe);
+    return new SecurityStandards(standards, cwe, owaspTop10, sansTop25, sq);
+  }
+
+  private static Set<String> toOwaspTop10(Set<String> securityStandards) {
+    return securityStandards.stream()
+      .filter(s -> s.startsWith(OWASP_TOP10_PREFIX))
+      .map(s -> s.substring(OWASP_TOP10_PREFIX.length()))
+      .collect(MoreCollectors.toSet());
+  }
+
+  private static Set<String> toCwe(Collection<String> securityStandards) {
+    Set<String> result = securityStandards.stream()
+      .filter(s -> s.startsWith(CWE_PREFIX))
+      .map(s -> s.substring(CWE_PREFIX.length()))
+      .collect(MoreCollectors.toSet());
+    return result.isEmpty() ? singleton(UNKNOWN_STANDARD) : result;
+  }
+
+  private static Set<String> toSansTop25(Collection<String> cwe) {
+    return CWES_BY_SANS_TOP_25
+      .keySet()
+      .stream()
+      .filter(k -> cwe.stream().anyMatch(CWES_BY_SANS_TOP_25.get(k)::contains))
+      .collect(MoreCollectors.toSet());
+  }
+
+  private static Set<String> toSonarSourceSecurityCategories(Collection<String> cwe) {
+    Set<String> 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;
+  }
+}
index 61aa2fb4ff3ae44a80ce79e1572bb7561b5a635e..c78c86cd4c05295ab272a4d7f2b5a123f9029f51 100644 (file)
@@ -48,6 +48,7 @@ import org.sonar.server.es.IndexingResult;
 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 static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
@@ -57,10 +58,8 @@ import static org.junit.rules.ExpectedException.none;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.server.issue.IssueDocTesting.newDoc;
 import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_OTHER_CWES_CATEGORY;
-import static org.sonar.server.security.SecurityStandardHelper.UNKNOWN_STANDARD;
 import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION;
+import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_POROUS_DEFENSES;
 
 public class IssueIndexerTest {
 
@@ -136,10 +135,10 @@ public class IssueIndexerTest {
     assertThat(doc.line()).isEqualTo(issue.getLine());
     // functional date
     assertThat(doc.updateDate()).isEqualToIgnoringMillis(new Date(issue.getIssueUpdateTime()));
-    assertThat(doc.getCwe()).containsExactlyInAnyOrder(UNKNOWN_STANDARD);
+    assertThat(doc.getCwe()).containsExactlyInAnyOrder(SecurityStandards.UNKNOWN_STANDARD);
     assertThat(doc.getOwaspTop10()).isEmpty();
     assertThat(doc.getSansTop25()).isEmpty();
-    assertThat(doc.getSonarSourceSecurityCategories()).containsExactlyInAnyOrder(SONARSOURCE_OTHER_CWES_CATEGORY);
+    assertThat(doc.getSonarSourceSecurityCategories()).containsOnly(SecurityStandards.SQ_OTHER_CATEGORY);
   }
 
   @Test
index 312948b0d35f16ec358223587412eb404827e375..d4280569e4e4b3f66fdec121d0e15b1aa542b209 100644 (file)
@@ -86,8 +86,8 @@ import static org.sonar.server.rule.index.RuleIndex.FACET_TYPES;
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE_EXTENSION;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE;
+import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
+import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_RISKY_RESOURCE;
 
 public class RuleIndexTest {
 
index 221fbe817a8ecb2d5bf23eaacf081c1fef1d14cf..7dba189141a0a3a2f96a4ab1c22631e19c2bee15 100644 (file)
@@ -82,6 +82,7 @@ import org.sonar.server.es.StickyFacetBuilder;
 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.user.UserSession;
 import org.sonar.server.view.index.ViewIndexDefinition;
 
@@ -152,12 +153,10 @@ import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_STAT
 import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TAGS;
 import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TYPE;
 import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_CWE_MAPPING;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_CWE_MAPPING;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_OTHER_CWES_CATEGORY;
+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;
@@ -874,15 +873,14 @@ 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(SANS_TOP_25_CWE_MAPPING))));
+      .forEach(sansCategory -> request.addAggregation(createAggregation(FIELD_ISSUE_SANS_TOP_25, sansCategory, includeCwe, Optional.of(SecurityStandards.CWES_BY_SANS_TOP_25))));
     return processSecurityReportSearchResults(request, includeCwe);
   }
 
   public List<SecurityStandardCategoryStatistics> getSonarSourceReport(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
     SearchRequestBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
-    Stream.concat(SONARSOURCE_CWE_MAPPING.keySet().stream(), Stream.of(SONARSOURCE_OTHER_CWES_CATEGORY))
-      .forEach(sonarsourceCategory -> request.addAggregation(
-        createAggregation(FIELD_ISSUE_SONARSOURCE_SECURITY, sonarsourceCategory, includeCwe, Optional.of(SONARSOURCE_CWE_MAPPING))));
+    SQ_CATEGORIES.forEach(sonarsourceCategory -> request.addAggregation(
+        createAggregation(FIELD_ISSUE_SONARSOURCE_SECURITY, sonarsourceCategory, includeCwe, Optional.of(SecurityStandards.CWES_BY_SQ_CATEGORY))));
     return processSecurityReportSearchResults(request, includeCwe);
   }
 
index 1b75d33423062e49c016de00719e51de35d8bd61..47915fff2cf056c74d8a7d88e650b990b08507f6 100644 (file)
@@ -54,10 +54,10 @@ import static org.sonar.api.resources.Qualifiers.PROJECT;
 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
 import static org.sonar.server.issue.IssueDocTesting.newDoc;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE;
-import static org.sonar.server.security.SecurityStandardHelper.UNKNOWN_STANDARD;
+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.UNKNOWN_STANDARD;
 
 public class IssueIndexSecurityReportsTest {
 
index f06ac22cdd42c6b7ccd7897d42f6345c1ded762a..ec3489534511857717d21004e1226024073f20fa 100644 (file)
@@ -54,6 +54,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.user.UserSession;
 import org.sonarqube.ws.Issues.SearchWsResponse;
 
@@ -84,12 +85,11 @@ import static org.sonar.server.issue.index.IssueIndex.FACET_ASSIGNED_TO_ME;
 import static org.sonar.server.issue.index.IssueIndex.FACET_PROJECTS;
 import static org.sonar.server.issue.index.IssueQuery.SORT_BY_ASSIGNEE;
 import static org.sonar.server.issue.index.IssueQueryFactory.UNKNOWN;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES;
-import static org.sonar.server.security.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_CWE_MAPPING;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_OTHER_CWES_CATEGORY;
-import static org.sonar.server.security.SecurityStandardHelper.UNKNOWN_STANDARD;
+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;
 import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_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 '" + SONARSOURCE_OTHER_CWES_CATEGORY + "' to select issues not associated" +
+      .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQ_OTHER_CATEGORY + "' to select issues not associated" +
         " with any category")
       .setSince("7.8")
-      .setPossibleValues(ImmutableList.builder().addAll(SONARSOURCE_CWE_MAPPING.keySet()).add(SONARSOURCE_OTHER_CWES_CATEGORY).build());
+      .setPossibleValues(SecurityStandards.SQ_CATEGORIES);
     action.createParam(DEPRECATED_PARAM_AUTHORS)
       .setDeprecatedSince("7.7")
       .setDescription("This parameter is deprecated, please use '%s' instead", PARAM_AUTHOR)
index cd1bb3c6f3a87911b331e8e96f570c46a331d267..bd44b41d8f02294bf9b9902f62892c5b855281bf 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.rule.ws;
 
-import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -39,6 +38,7 @@ import org.sonar.db.user.UserDto;
 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.user.UserSession;
 
 import static org.sonar.api.server.ws.WebService.Param.ASCENDING;
@@ -50,6 +50,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
 import static org.sonar.db.organization.OrganizationDto.Subscription.PAID;
 import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
+import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional;
 import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION;
 import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES;
 import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE;
@@ -71,11 +72,7 @@ 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.SecurityStandardHelper.SANS_TOP_25_CWE_MAPPING;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_CWE_MAPPING;
-import static org.sonar.server.security.SecurityStandardHelper.SONARSOURCE_OTHER_CWES_CATEGORY;
-import static org.sonar.server.security.SecurityStandardHelper.UNKNOWN_STANDARD;
-import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional;
+import static org.sonar.server.security.SecurityStandards.SQ_OTHER_CATEGORY;
 
 @ServerSide
 public class RuleWsSupport {
@@ -141,8 +138,8 @@ public class RuleWsSupport {
 
     action
       .createParam(PARAM_CWE)
-      .setDescription("Comma-separated list of CWE identifiers. Use '" + UNKNOWN_STANDARD + "' to select rules not associated to any CWE.")
-      .setExampleValue("12,125," + UNKNOWN_STANDARD);
+      .setDescription("Comma-separated list of CWE identifiers. Use '" + SecurityStandards.UNKNOWN_STANDARD + "' to select rules not associated to any CWE.")
+      .setExampleValue("12,125," + SecurityStandards.UNKNOWN_STANDARD);
 
     action.createParam(PARAM_OWASP_TOP_10)
       .setDescription("Comma-separated list of OWASP Top 10 lowercase categories.")
@@ -152,14 +149,14 @@ public class RuleWsSupport {
     action.createParam(PARAM_SANS_TOP_25)
       .setDescription("Comma-separated list of SANS Top 25 categories.")
       .setSince("7.3")
-      .setPossibleValues(SANS_TOP_25_CWE_MAPPING.keySet());
+      .setPossibleValues(SecurityStandards.CWES_BY_SANS_TOP_25.keySet());
 
     action
       .createParam(PARAM_SONARSOURCE_SECURITY)
-      .setDescription("Comma-separated list of SonarSource security categories. Use '" + SONARSOURCE_OTHER_CWES_CATEGORY + "' to select rules not associated" +
+      .setDescription("Comma-separated list of SonarSource security categories. Use '" + SQ_OTHER_CATEGORY + "' to select rules not associated" +
         " with any category")
       .setSince("7.8")
-      .setPossibleValues(ImmutableList.builder().addAll(SONARSOURCE_CWE_MAPPING.keySet()).add(SONARSOURCE_OTHER_CWES_CATEGORY).build())
+      .setPossibleValues(SecurityStandards.SQ_CATEGORIES)
       .setExampleValue("sql-injection,command-injection,others");
 
     action