@@ -19,17 +19,12 @@ | |||
*/ | |||
package org.sonar.server.issue.index; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.config.internal.MapSettings; | |||
import org.sonar.server.es.IndexDefinition; | |||
import org.sonar.server.es.IndexType; | |||
import org.sonar.server.es.NewIndex; | |||
import static java.util.Arrays.asList; | |||
import static org.sonar.server.es.DefaultIndexSettingsElement.SORTABLE_ANALYZER; | |||
import static org.sonar.server.es.NewIndex.SettingsConfiguration.MANUAL_REFRESH_INTERVAL; | |||
import static org.sonar.server.es.NewIndex.SettingsConfiguration.newBuilder; | |||
@@ -100,18 +95,6 @@ public class IssueIndexDefinition implements IndexDefinition { | |||
public static final String FIELD_ISSUE_OWASP_TOP_10 = "owaspTop10"; | |||
public static final String FIELD_ISSUE_SANS_TOP_25 = "sansTop25"; | |||
public static final String FIELD_ISSUE_CWE = "cwe"; | |||
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"; | |||
// 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")); | |||
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); | |||
private final Configuration config; | |||
private final boolean enableSource; |
@@ -27,7 +27,6 @@ import java.sql.PreparedStatement; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
@@ -42,11 +41,12 @@ import org.sonar.db.DbSession; | |||
import org.sonar.db.ResultSetIterator; | |||
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.server.issue.index.IssueIndexDefinition.SANS_TOP_25_CWE_MAPPING; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.UNKNOWN_STANDARD; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.getCwe; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.getOwaspTop10; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.getSansTop25; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.getSecurityStandards; | |||
/** | |||
* Scrolls over table ISSUES and reads documents to populate | |||
@@ -94,10 +94,7 @@ class IssueIteratorForSingleChunk implements IssueIterator { | |||
private static final String ISSUE_KEY_FILTER_SUFFIX = ")"; | |||
static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | |||
static final Splitter SECURITY_STANDARDS_SPLITTER = TAGS_SPLITTER; | |||
static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings(); | |||
private static final String OWASP_TOP10_PREFIX = "owaspTop10:"; | |||
private static final String CWE_PREFIX = "cwe:"; | |||
private final DbSession session; | |||
@@ -235,23 +232,14 @@ class IssueIteratorForSingleChunk implements IssueIterator { | |||
doc.setType(RuleType.valueOf(rs.getInt(22))); | |||
String securityStandards = rs.getString(23); | |||
List<String> standards = IssueIteratorForSingleChunk.SECURITY_STANDARDS_SPLITTER.splitToList(securityStandards == null ? "" : securityStandards); | |||
List<String> owaspTop10 = standards.stream().filter(s -> s.startsWith(OWASP_TOP10_PREFIX)).map(s -> s.substring(OWASP_TOP10_PREFIX.length())).collect(toList()); | |||
doc.setOwaspTop10(owaspTop10.isEmpty() ? Collections.singletonList(UNKNOWN_STANDARD) : owaspTop10); | |||
List<String> cwe = standards.stream().filter(s -> s.startsWith(CWE_PREFIX)).map(s -> s.substring(CWE_PREFIX.length())).collect(toList()); | |||
doc.setCwe(cwe.isEmpty() ? Collections.singletonList(UNKNOWN_STANDARD) : cwe); | |||
List<String> standards = getSecurityStandards(securityStandards); | |||
doc.setOwaspTop10(getOwaspTop10(standards)); | |||
List<String> cwe = getCwe(standards); | |||
doc.setCwe(cwe); | |||
doc.setSansTop25(getSansTop25(cwe)); | |||
return doc; | |||
} | |||
private static List<String> getSansTop25(List<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()); | |||
} | |||
@CheckForNull | |||
private static String extractDirPath(@Nullable String filePath, String scope) { | |||
if (filePath != null) { |
@@ -32,6 +32,8 @@ public class SecurityStandardCategoryStatistics { | |||
private final long openSecurityHotspots; | |||
private final long wontFixSecurityHotspots; | |||
private final List<SecurityStandardCategoryStatistics> children; | |||
private long activeRules; | |||
private long totalRules; | |||
public SecurityStandardCategoryStatistics(String category, long vulnerabilities, OptionalInt vulnerabiliyRating, long toReviewSecurityHotspots, long openSecurityHotspots, | |||
long wontFixSecurityHotspots, @Nullable List<SecurityStandardCategoryStatistics> children) { | |||
@@ -71,4 +73,22 @@ public class SecurityStandardCategoryStatistics { | |||
public List<SecurityStandardCategoryStatistics> getChildren() { | |||
return children; | |||
} | |||
public long getActiveRules() { | |||
return activeRules; | |||
} | |||
public long getTotalRules() { | |||
return totalRules; | |||
} | |||
public SecurityStandardCategoryStatistics setActiveRules(long activeRules) { | |||
this.activeRules = activeRules; | |||
return this; | |||
} | |||
public SecurityStandardCategoryStatistics setTotalRules(long totalRules) { | |||
this.totalRules = totalRules; | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,101 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.issue.index; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.ImmutableMap; | |||
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); | |||
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> getOwaspTop10(Collection<String> securityStandards) { | |||
List<String> result = securityStandards.stream() | |||
.filter(s -> s.startsWith(OWASP_TOP10_PREFIX)) | |||
.map(s -> s.substring(OWASP_TOP10_PREFIX.length())) | |||
.collect(toList()); | |||
return result.isEmpty() ? singletonList(UNKNOWN_STANDARD) : result; | |||
} | |||
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> getOwaspTop10(String securityStandards) { | |||
return getOwaspTop10(getSecurityStandards(securityStandards)); | |||
} | |||
public static List<String> getCwe(String securityStandards) { | |||
return getCwe(getSecurityStandards(securityStandards)); | |||
} | |||
} |
@@ -57,8 +57,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.INDEX_TYPE_ISSUE; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.UNKNOWN_STANDARD; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.UNKNOWN_STANDARD; | |||
import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION; | |||
public class IssueIndexerTest { |
@@ -143,10 +143,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.INDEX_TYPE_ISSUE; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.UNKNOWN_STANDARD; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.UNKNOWN_STANDARD; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHORS; |
@@ -73,10 +73,10 @@ import static org.sonar.process.ProcessProperties.Property.SONARCLOUD_ENABLED; | |||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | |||
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.IssueIndexDefinition.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.UNKNOWN_STANDARD; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.UNKNOWN_STANDARD; | |||
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.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; |
@@ -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.issue.index.IssueIndexDefinition.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.IssueIndexDefinition.UNKNOWN_STANDARD; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_INSECURE_INTERACTION; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_POROUS_DEFENSES; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.SANS_TOP_25_RISKY_RESOURCE; | |||
import static org.sonar.server.issue.index.SecurityStandardHelper.UNKNOWN_STANDARD; | |||
public class IssueIndexSecurityReportsTest { | |||