Procházet zdrojové kódy

SONAR-12717 a rule must map to a single SQ Security Category

if it's not the case, only one is taken into account
a WARN log is displayed at startup to indicate rules wich do not comply
tags/8.2.0.32929
Sébastien Lesaint před 4 roky
rodič
revize
f816ceb0a3

+ 6
- 4
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueDoc.java Zobrazit soubor

@@ -30,6 +30,7 @@ import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.server.es.BaseDoc;
import org.sonar.server.permission.index.AuthorizationDoc;
import org.sonar.server.security.SecurityStandards;

import static org.sonar.server.issue.index.IssueIndexDefinition.TYPE_ISSUE;

@@ -315,12 +316,13 @@ public class IssueDoc extends BaseDoc {
}

@CheckForNull
public Collection<String> getSonarSourceSecurityCategories() {
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_SONARSOURCE_SECURITY);
public SecurityStandards.SQCategory getSonarSourceSecurityCategory() {
String key = getNullableField(IssueIndexDefinition.FIELD_ISSUE_SONARSOURCE_SECURITY);
return SecurityStandards.SQCategory.fromKey(key).orElse(null);
}

public IssueDoc setSonarSourceSecurityCategories(@Nullable Collection<String> c) {
setField(IssueIndexDefinition.FIELD_ISSUE_SONARSOURCE_SECURITY, c);
public IssueDoc setSonarSourceSecurityCategory(@Nullable SecurityStandards.SQCategory c) {
setField(IssueIndexDefinition.FIELD_ISSUE_SONARSOURCE_SECURITY, c == null ? null : c.getKey());
return this;
}
}

+ 1
- 3
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java Zobrazit soubor

@@ -39,10 +39,8 @@ 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;
@@ -235,7 +233,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {
doc.setOwaspTop10(securityStandards.getOwaspTop10());
doc.setCwe(securityStandards.getCwe());
doc.setSansTop25(securityStandards.getSansTop25());
doc.setSonarSourceSecurityCategories(securityStandards.getSq().stream().map(SQCategory::getKey).collect(toList()));
doc.setSonarSourceSecurityCategory(securityStandards.getSqCategory());
return doc;
}


+ 7
- 8
server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java Zobrazit soubor

@@ -38,7 +38,6 @@ 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;


@@ -188,12 +187,13 @@ public class RuleDoc extends BaseDoc {
}

@CheckForNull
public Collection<String> getSonarSourceSecurityCategories() {
return getNullableField(RuleIndexDefinition.FIELD_RULE_SONARSOURCE_SECURITY);
public SQCategory getSonarSourceSecurityCategory() {
String key = getNullableField(RuleIndexDefinition.FIELD_RULE_SONARSOURCE_SECURITY);
return SQCategory.fromKey(key).orElse(null);
}

public RuleDoc setSonarSourceSecurityCategories(@Nullable Collection<String> c) {
setField(RuleIndexDefinition.FIELD_RULE_SONARSOURCE_SECURITY, c);
public RuleDoc setSonarSourceSecurityCategory(@Nullable SQCategory sqCategory) {
setField(RuleIndexDefinition.FIELD_RULE_SONARSOURCE_SECURITY, sqCategory == null ? null : sqCategory.getKey());
return this;
}

@@ -269,8 +269,7 @@ public class RuleDoc extends BaseDoc {
return ReflectionToStringBuilder.toString(this);
}

public static RuleDoc of(RuleForIndexingDto dto) {
SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(dto.getSecurityStandards());
public static RuleDoc of(RuleForIndexingDto dto, SecurityStandards securityStandards) {
RuleDoc ruleDoc = new RuleDoc()
.setId(dto.getId())
.setKey(dto.getRuleKey().toString())
@@ -282,7 +281,7 @@ public class RuleDoc extends BaseDoc {
.setCwe(securityStandards.getCwe())
.setOwaspTop10(securityStandards.getOwaspTop10())
.setSansTop25(securityStandards.getSansTop25())
.setSonarSourceSecurityCategories(securityStandards.getSq().stream().map(SQCategory::getKey).collect(toList()))
.setSonarSourceSecurityCategory(securityStandards.getSqCategory())
.setName(dto.getName())
.setRuleKey(dto.getPluginRuleKey())
.setSeverity(dto.getSeverityAsString())

+ 24
- 3
server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndexer.java Zobrazit soubor

@@ -25,12 +25,15 @@ import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.es.EsQueueDto;
import org.sonar.db.es.RuleExtensionId;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleForIndexingDto;
import org.sonar.server.es.BulkIndexer;
import org.sonar.server.es.BulkIndexer.Size;
import org.sonar.server.es.EsClient;
@@ -39,16 +42,19 @@ import org.sonar.server.es.IndexingListener;
import org.sonar.server.es.IndexingResult;
import org.sonar.server.es.OneToOneResilientIndexingListener;
import org.sonar.server.es.ResilientIndexer;
import org.sonar.server.security.SecurityStandards;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.concat;
import static org.sonar.core.util.stream.MoreCollectors.toHashSet;
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.SecurityStandards.SQ_CATEGORY_KEYS_ORDERING;

public class RuleIndexer implements ResilientIndexer {

private final EsClient esClient;
private final DbClient dbClient;

@@ -71,7 +77,7 @@ public class RuleIndexer implements ResilientIndexer {
// index all definitions and system extensions
if (uninitializedIndexTypes.contains(TYPE_RULE)) {
dbClient.ruleDao().scrollIndexingRules(dbSession, dto -> {
bulk.add(RuleDoc.of(dto).toIndexRequest());
bulk.add(ruleDocOf(dto).toIndexRequest());
bulk.add(RuleExtensionDoc.of(dto).toIndexRequest());
});
}
@@ -142,7 +148,7 @@ public class RuleIndexer implements ResilientIndexer {

dbClient.ruleDao().scrollIndexingRulesByKeys(dbSession, ruleIds,
r -> {
bulkIndexer.add(RuleDoc.of(r).toIndexRequest());
bulkIndexer.add(ruleDocOf(r).toIndexRequest());
bulkIndexer.add(RuleExtensionDoc.of(r).toIndexRequest());
ruleIds.remove(r.getId());
});
@@ -186,6 +192,21 @@ public class RuleIndexer implements ResilientIndexer {
return Optional.of(bulkIndexer.stop());
}

private RuleDoc ruleDocOf(RuleForIndexingDto dto) {
SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(dto.getSecurityStandards());
if (!securityStandards.getIgnoredSQCategories().isEmpty()) {
Loggers.get(RuleIndexer.class).warn(
"Rule {} with CWEs '{}' maps to multiple SQ Security Categories: {}",
dto.getRuleKey(),
String.join(", ", securityStandards.getCwe()),
concat(Stream.of(securityStandards.getSqCategory()), securityStandards.getIgnoredSQCategories().stream())
.map(SecurityStandards.SQCategory::getKey)
.sorted(SQ_CATEGORY_KEYS_ORDERING)
.collect(joining(", ")));
}
return RuleDoc.of(dto, securityStandards);
}

private BulkIndexer createBulkIndexer(Size bulkSize, IndexingListener listener) {
return new BulkIndexer(esClient, TYPE_RULE, bulkSize, listener);
}

+ 43
- 20
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityStandards.java Zobrazit soubor

@@ -22,18 +22,24 @@ package org.sonar.server.security;
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.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 javax.annotation.concurrent.Immutable;
import org.sonar.core.util.stream.MoreCollectors;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.server.security.SecurityStandards.VulnerabilityProbability.HIGH;
import static org.sonar.server.security.SecurityStandards.VulnerabilityProbability.LOW;
import static org.sonar.server.security.SecurityStandards.VulnerabilityProbability.MEDIUM;
@@ -85,6 +91,7 @@ public final class SecurityStandards {
FILE_MANIPULATION("file-manipulation", LOW),
OTHERS("others", LOW);

private static final Map<String, SQCategory> SQ_CATEGORY_BY_KEY = stream(values()).collect(uniqueIndex(SQCategory::getKey));
private final String key;
private final VulnerabilityProbability vulnerability;

@@ -100,6 +107,10 @@ public final class SecurityStandards {
public VulnerabilityProbability getVulnerability() {
return vulnerability;
}

public static Optional<SQCategory> fromKey(@Nullable String key) {
return Optional.ofNullable(key).map(SQ_CATEGORY_BY_KEY::get);
}
}

public static final Map<SQCategory, Set<String>> CWES_BY_SQ_CATEGORY = ImmutableMap.<SQCategory, Set<String>>builder()
@@ -123,21 +134,23 @@ public final class SecurityStandards {
.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 Ordering<SQCategory> SQ_CATEGORY_ORDERING = Ordering.explicit(Arrays.stream(SQCategory.values()).collect(Collectors.toList()));
public static final Ordering<String> SQ_CATEGORY_KEYS_ORDERING = Ordering.explicit(Arrays.stream(SQCategory.values()).map(SQCategory::getKey).collect(Collectors.toList()));
private static final Ordering<SQCategory> SQ_CATEGORY_ORDERING = Ordering.explicit(stream(SQCategory.values()).collect(Collectors.toList()));
public static final Ordering<String> SQ_CATEGORY_KEYS_ORDERING = Ordering.explicit(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<SQCategory> sq;
private final SQCategory sqCategory;
private final Set<SQCategory> ignoredSQCategories;

private SecurityStandards(Set<String> standards, Set<String> cwe, Set<String> owaspTop10, Set<String> sansTop25, Set<SQCategory> sq) {
private SecurityStandards(Set<String> standards, Set<String> cwe, Set<String> owaspTop10, Set<String> sansTop25, SQCategory sqCategory, Set<SQCategory> ignoredSQCategories) {
this.standards = standards;
this.cwe = cwe;
this.owaspTop10 = owaspTop10;
this.sansTop25 = sansTop25;
this.sq = sq;
this.sqCategory = sqCategory;
this.ignoredSQCategories = ignoredSQCategories;
}

public Set<String> getStandards() {
@@ -156,33 +169,42 @@ public final class SecurityStandards {
return sansTop25;
}

public Set<SQCategory> getSq() {
return sq;
public SQCategory getSqCategory() {
return sqCategory;
}

public Set<SQCategory> getIgnoredSQCategories() {
return ignoredSQCategories;
}

/**
* @throws IllegalStateException if {@code securityStandards} maps to multiple {@link SQCategory SQCategories}
*/
public static SecurityStandards fromSecurityStandards(Set<String> securityStandards) {
Set<String> standards = securityStandards.stream()
.filter(Objects::nonNull)
.collect(MoreCollectors.toSet());
Set<String> owaspTop10 = toOwaspTop10(standards);
.collect(toSet());
Set<String> cwe = toCwe(standards);
Set<String> owaspTop10 = toOwaspTop10(standards);
Set<String> sansTop25 = toSansTop25(cwe);
Set<SQCategory> sq = toSQCategories(cwe);
return new SecurityStandards(standards, cwe, owaspTop10, sansTop25, sq);
List<SQCategory> sq = toSortedSQCategories(cwe);
SQCategory sqCategory = sq.iterator().next();
Set<SQCategory> ignoredSQCategories = sq.stream().skip(1).collect(Collectors.toSet());
return new SecurityStandards(standards, cwe, owaspTop10, sansTop25, sqCategory, ignoredSQCategories);
}

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());
.collect(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());
.collect(toSet());
return result.isEmpty() ? singleton(UNKNOWN_STANDARD) : result;
}

@@ -191,15 +213,16 @@ public final class SecurityStandards {
.keySet()
.stream()
.filter(k -> cwe.stream().anyMatch(CWES_BY_SANS_TOP_25.get(k)::contains))
.collect(MoreCollectors.toSet());
.collect(toSet());
}

private static Set<SQCategory> toSQCategories(Collection<String> cwe) {
Set<SQCategory> result = CWES_BY_SQ_CATEGORY
private static List<SQCategory> toSortedSQCategories(Collection<String> cwe) {
List<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(SQCategory.OTHERS) : result;
.sorted(SQ_CATEGORY_ORDERING)
.collect(toList());
return result.isEmpty() ? singletonList(SQCategory.OTHERS) : result;
}
}

+ 1
- 1
server/sonar-server-common/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java Zobrazit soubor

@@ -139,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(SQCategory.OTHERS.getKey());
assertThat(doc.getSonarSourceSecurityCategory()).isEqualTo(SQCategory.OTHERS);
}

@Test

+ 60
- 4
server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java Zobrazit soubor

@@ -20,13 +20,22 @@
package org.sonar.server.rule.index;

import com.google.common.collect.ImmutableSet;
import java.util.stream.Collectors;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.EnumSet;
import java.util.Random;
import java.util.Set;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
@@ -37,22 +46,31 @@ import org.sonar.db.rule.RuleDto.Scope;
import org.sonar.db.rule.RuleMetadataDto;
import org.sonar.db.rule.RuleTesting;
import org.sonar.server.es.EsTester;
import org.sonar.server.security.SecurityStandards;
import org.sonar.server.security.SecurityStandards.SQCategory;

import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
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.SecurityStandards.CWES_BY_SQ_CATEGORY;
import static org.sonar.server.security.SecurityStandards.SQ_CATEGORY_KEYS_ORDERING;

@RunWith(DataProviderRunner.class)
public class RuleIndexerTest {

@Rule
public EsTester es = EsTester.create();

@Rule
public DbTester dbTester = DbTester.create();
@Rule
public LogTester logTester = new LogTester();

private DbClient dbClient = dbTester.getDbClient();
private final RuleIndexer underTest = new RuleIndexer(es.client(), dbClient);
@@ -122,7 +140,7 @@ public class RuleIndexerTest {
.get()
.getHits()
.getHits()[0]
.getId()).isEqualTo(doc.getId());
.getId()).isEqualTo(doc.getId());
}

@Test
@@ -149,10 +167,48 @@ public class RuleIndexerTest {

@Test
public void index_long_rule_description() {
String description = IntStream.range(0, 100000).map(i -> i % 100).mapToObj(Integer::toString).collect(Collectors.joining(" "));
String description = IntStream.range(0, 100000).map(i -> i % 100).mapToObj(Integer::toString).collect(joining(" "));
RuleDefinitionDto rule = dbTester.rules().insert(r -> r.setDescription(description));
underTest.commitAndIndex(dbTester.getSession(), rule.getId());

assertThat(es.countDocuments(TYPE_RULE)).isEqualTo(1);
}

@Test
@UseDataProvider("twoDifferentCategoriesButOTHERS")
public void log_a_warning_if_hotspot_rule_maps_to_multiple_SQCategories(SQCategory sqCategory1, SQCategory sqCategory2) {
Set<String> standards = Stream.of(sqCategory1, sqCategory2)
.flatMap(t -> CWES_BY_SQ_CATEGORY.get(t).stream().map(e -> "cwe:" + e))
.collect(toSet());
SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(standards);
RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule().setType(RuleType.SECURITY_HOTSPOT).setSecurityStandards(standards));
OrganizationDto organization = dbTester.organizations().insert();
underTest.commitAndIndex(dbTester.getSession(), rule.getId(), organization);

assertThat(logTester.getLogs()).hasSize(1);
assertThat(logTester.logs(LoggerLevel.WARN).get(0))
.isEqualTo(format(
"Rule %s with CWEs '%s' maps to multiple SQ Security Categories: %s",
rule.getKey(),
String.join(", ", securityStandards.getCwe()),
ImmutableSet.of(sqCategory1, sqCategory2).stream()
.map(SQCategory::getKey)
.sorted(SQ_CATEGORY_KEYS_ORDERING)
.collect(joining(", "))));
}

@DataProvider
public static Object[][] twoDifferentCategoriesButOTHERS() {
EnumSet<SQCategory> sqCategories = EnumSet.allOf(SQCategory.class);
sqCategories.remove(SQCategory.OTHERS);

// pick two random categories
Random random = new Random();
SQCategory sqCategory1 = sqCategories.toArray(new SQCategory[0])[random.nextInt(sqCategories.size())];
sqCategories.remove(sqCategory1);
SQCategory sqCategory2 = sqCategories.toArray(new SQCategory[0])[random.nextInt(sqCategories.size())];
return new Object[][] {
{sqCategory1, sqCategory2}
};
}
}

+ 110
- 0
server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityStandardsTest.java Zobrazit soubor

@@ -0,0 +1,110 @@
/*
* 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 java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
import org.sonar.server.security.SecurityStandards.SQCategory;

import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.security.SecurityStandards.CWES_BY_SQ_CATEGORY;
import static org.sonar.server.security.SecurityStandards.SQ_CATEGORY_KEYS_ORDERING;
import static org.sonar.server.security.SecurityStandards.fromSecurityStandards;

public class SecurityStandardsTest {
@Test
public void fromSecurityStandards_from_empty_set_has_SQCategory_OTHERS() {
SecurityStandards securityStandards = fromSecurityStandards(emptySet());

assertThat(securityStandards.getStandards()).isEmpty();
assertThat(securityStandards.getSqCategory()).isEqualTo(SQCategory.OTHERS);
assertThat(securityStandards.getIgnoredSQCategories()).isEmpty();
}

@Test
public void fromSecurityStandards_from_empty_set_has_unkwown_cwe_standard() {
SecurityStandards securityStandards = fromSecurityStandards(emptySet());

assertThat(securityStandards.getStandards()).isEmpty();
assertThat(securityStandards.getCwe()).containsOnly("unknown");
}

@Test
public void fromSecurityStandards_from_empty_set_has_no_OwaspTop10_standard() {
SecurityStandards securityStandards = fromSecurityStandards(emptySet());

assertThat(securityStandards.getStandards()).isEmpty();
assertThat(securityStandards.getOwaspTop10()).isEmpty();
}

@Test
public void fromSecurityStandards_from_empty_set_has_no_SansTop25_standard() {
SecurityStandards securityStandards = fromSecurityStandards(emptySet());

assertThat(securityStandards.getStandards()).isEmpty();
assertThat(securityStandards.getSansTop25()).isEmpty();
}

@Test
public void fromSecurityStandards_finds_SQCategory_from_any_if_the_mapped_CWE_standard() {
CWES_BY_SQ_CATEGORY.forEach((sqCategory, cwes) -> {
cwes.forEach(cwe -> {
SecurityStandards securityStandards = fromSecurityStandards(singleton("cwe:" + cwe));

assertThat(securityStandards.getSqCategory()).isEqualTo(sqCategory);
});
});
}

@Test
public void fromSecurityStandards_finds_SQCategory_from_multiple_of_the_mapped_CWE_standard() {
CWES_BY_SQ_CATEGORY.forEach((sqCategory, cwes) -> {
SecurityStandards securityStandards = fromSecurityStandards(cwes.stream().map(t -> "cwe:" + t).collect(toSet()));

assertThat(securityStandards.getSqCategory()).isEqualTo(sqCategory);
});
}

@Test
public void fromSecurityStandards_finds_SQCategory_first_in_order_when_CWEs_map_to_multiple_SQCategories() {
EnumSet<SQCategory> sqCategories = EnumSet.allOf(SQCategory.class);
sqCategories.remove(SQCategory.OTHERS);

while (!sqCategories.isEmpty()) {
SQCategory expected = sqCategories.stream().min(SQ_CATEGORY_KEYS_ORDERING.onResultOf(SQCategory::getKey)).get();
SQCategory[] expectedIgnored = sqCategories.stream().filter(t -> t != expected).toArray(SQCategory[]::new);

Set<String> cwes = sqCategories.stream()
.flatMap(t -> CWES_BY_SQ_CATEGORY.get(t).stream().map(e -> "cwe:" + e))
.collect(Collectors.toSet());
SecurityStandards securityStandards = fromSecurityStandards(cwes);

assertThat(securityStandards.getSqCategory()).isEqualTo(expected);
assertThat(securityStandards.getIgnoredSQCategories()).containsOnly(expectedIgnored);

sqCategories.remove(expected);
}
}
}

+ 1
- 4
server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/SearchAction.java Zobrazit soubor

@@ -236,10 +236,7 @@ public class SearchAction implements HotspotsWsAction {
Hotspots.Rule.Builder ruleBuilder = Hotspots.Rule.newBuilder();
for (RuleDefinitionDto rule : rules) {
SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(rule.getSecurityStandards());
SecurityStandards.SQCategory sqCategory = securityStandards.getSq()
.stream()
.min(SecurityStandards.SQ_CATEGORY_ORDERING)
.orElse(SecurityStandards.SQCategory.OTHERS);
SecurityStandards.SQCategory sqCategory = securityStandards.getSqCategory();
ruleBuilder
.clear()
.setKey(rule.getKey().toString())

Načítá se…
Zrušit
Uložit