return getNullableField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10);
}
+ @CheckForNull
+ public Collection<String> getOwaspTop10For2021() {
+ return getNullableField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10_2021);
+ }
+
public IssueDoc setOwaspTop10(@Nullable Collection<String> o) {
setField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10, o);
return this;
}
+ public IssueDoc setOwaspTop10For2021(@Nullable Collection<String> o) {
+ setField(IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10_2021, o);
+ return this;
+ }
+
@CheckForNull
public Collection<String> getSansTop25() {
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_SANS_TOP_25);
public static final String FIELD_ISSUE_TAGS = "tags";
public static final String FIELD_ISSUE_TYPE = "type";
public static final String FIELD_ISSUE_OWASP_TOP_10 = "owaspTop10";
+ public static final String FIELD_ISSUE_OWASP_TOP_10_2021 = "owaspTop10-2021";
public static final String FIELD_ISSUE_SANS_TOP_25 = "sansTop25";
public static final String FIELD_ISSUE_CWE = "cwe";
public static final String FIELD_ISSUE_SQ_SECURITY_CATEGORY = "sonarsourceSecurity";
mapping.keywordFieldBuilder(FIELD_ISSUE_TAGS).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_TYPE).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_OWASP_TOP_10).disableNorms().build();
+ mapping.keywordFieldBuilder(FIELD_ISSUE_OWASP_TOP_10_2021).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_SANS_TOP_25).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_CWE).disableNorms().build();
mapping.keywordFieldBuilder(FIELD_ISSUE_SQ_SECURITY_CATEGORY).disableNorms().build();
SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(rs.getString(22)));
SecurityStandards.SQCategory sqCategory = securityStandards.getSqCategory();
doc.setOwaspTop10(securityStandards.getOwaspTop10());
+ doc.setOwaspTop10For2021(securityStandards.getOwaspTop10For2021());
doc.setCwe(securityStandards.getCwe());
doc.setSansTop25(securityStandards.getSansTop25());
doc.setSonarSourceSecurityCategory(sqCategory);
public static final String SANS_TOP_25_POROUS_DEFENSES = "porous-defenses";
private static final String OWASP_TOP10_PREFIX = "owaspTop10:";
+ private static final String OWASP_TOP10_2021_PREFIX = "owaspTop10-2021:";
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"));
}
public Set<String> getOwaspTop10() {
- return toOwaspTop10(standards);
+ return toOwaspTop10(standards, OWASP_TOP10_PREFIX);
+ }
+
+ public Set<String> getOwaspTop10For2021() {
+ return toOwaspTop10(standards, OWASP_TOP10_2021_PREFIX);
}
/**
return new SecurityStandards(standards, cwe, sqCategory, ignoredSQCategories);
}
- private static Set<String> toOwaspTop10(Set<String> securityStandards) {
+ private static Set<String> toOwaspTop10(Set<String> securityStandards, String prefix) {
return securityStandards.stream()
- .filter(s -> s.startsWith(OWASP_TOP10_PREFIX))
- .map(s -> s.substring(OWASP_TOP10_PREFIX.length()))
+ .filter(s -> s.startsWith(prefix))
+ .map(s -> s.substring(prefix.length()))
.collect(toSet());
}
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.core.util.stream.MoreCollectors;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.server.es.BaseDoc.epochMillisToEpochSeconds;
import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
import static org.sonar.server.issue.index.IssueIndex.Facet.LANGUAGES;
import static org.sonar.server.issue.index.IssueIndex.Facet.MODULE_UUIDS;
import static org.sonar.server.issue.index.IssueIndex.Facet.OWASP_TOP_10;
+import static org.sonar.server.issue.index.IssueIndex.Facet.OWASP_TOP_10_2021;
import static org.sonar.server.issue.index.IssueIndex.Facet.PROJECT_UUIDS;
import static org.sonar.server.issue.index.IssueIndex.Facet.RESOLUTIONS;
import static org.sonar.server.issue.index.IssueIndex.Facet.RULES;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_NEW_CODE_REFERENCE;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_OWASP_TOP_10_2021;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RESOLUTION;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_RULE_UUID;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10_2021;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
ASSIGNEES(PARAM_ASSIGNEES, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, MAX_FACET_SIZE),
ASSIGNED_TO_ME(FACET_ASSIGNED_TO_ME, FIELD_ISSUE_ASSIGNEE_UUID, STICKY, 1),
OWASP_TOP_10(PARAM_OWASP_TOP_10, FIELD_ISSUE_OWASP_TOP_10, STICKY, DEFAULT_FACET_SIZE),
+ OWASP_TOP_10_2021(PARAM_OWASP_TOP_10_2021, FIELD_ISSUE_OWASP_TOP_10_2021, STICKY, DEFAULT_FACET_SIZE),
SANS_TOP_25(PARAM_SANS_TOP_25, FIELD_ISSUE_SANS_TOP_25, STICKY, DEFAULT_FACET_SIZE),
CWE(PARAM_CWE, FIELD_ISSUE_CWE, STICKY, DEFAULT_FACET_SIZE),
CREATED_AT(PARAM_CREATED_AT, FIELD_ISSUE_FUNC_CREATED_AT, NON_STICKY),
// security category
addSecurityCategoryFilter(FIELD_ISSUE_OWASP_TOP_10, OWASP_TOP_10, query.owaspTop10(), filters);
+ addSecurityCategoryFilter(FIELD_ISSUE_OWASP_TOP_10_2021, OWASP_TOP_10_2021, query.owaspTop10For2021(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_SANS_TOP_25, SANS_TOP_25, query.sansTop25(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_CWE, CWE, query.cwe(), filters);
addSecurityCategoryFilter(FIELD_ISSUE_SQ_SECURITY_CATEGORY, SONARSOURCE_SECURITY, query.sonarsourceSecurity(), filters);
AggregationBuilders.filter(sansCategory, boolQuery().filter(termQuery(FIELD_ISSUE_SANS_TOP_25, sansCategory))),
includeCwe,
SecurityStandards.CWES_BY_SANS_TOP_25.get(sansCategory))));
- return processSecurityReportSearchResults(request, includeCwe);
+ return search(request, includeCwe);
}
public List<SecurityStandardCategoryStatistics> getCweTop25Reports(String projectUuid, boolean isViewOrApp) {
AggregationBuilders.filter(cweYear, boolQuery().filter(existsQuery(FIELD_ISSUE_CWE))),
true,
CWES_BY_CWE_TOP_25.get(cweYear))));
- List<SecurityStandardCategoryStatistics> result = processSecurityReportSearchResults(request, true);
+ List<SecurityStandardCategoryStatistics> result = search(request, true);
for (SecurityStandardCategoryStatistics cweReport : result) {
Set<String> foundRules = cweReport.getChildren().stream()
.map(SecurityStandardCategoryStatistics::getCategory)
AggregationBuilders.filter(sonarsourceCategory.getKey(), boolQuery().filter(termQuery(FIELD_ISSUE_SQ_SECURITY_CATEGORY, sonarsourceCategory.getKey()))),
includeCwe,
SecurityStandards.CWES_BY_SQ_CATEGORY.get(sonarsourceCategory))));
- return processSecurityReportSearchResults(request, includeCwe);
+ return search(request, includeCwe);
}
- public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe) {
+ public List<SecurityStandardCategoryStatistics> getOwaspTop10Report(String projectUuid, boolean isViewOrApp, boolean includeCwe, OwaspTop10Version version) {
+ String queryKey = version == Y2021 ? FIELD_ISSUE_OWASP_TOP_10_2021 : FIELD_ISSUE_OWASP_TOP_10;
SearchSourceBuilder request = prepareNonClosedVulnerabilitiesAndHotspotSearch(projectUuid, isViewOrApp);
IntStream.rangeClosed(1, 10).mapToObj(i -> "a" + i)
.forEach(owaspCategory -> request.aggregation(
newSecurityReportSubAggregations(
- AggregationBuilders.filter(owaspCategory, boolQuery().filter(termQuery(FIELD_ISSUE_OWASP_TOP_10, owaspCategory))),
+ AggregationBuilders.filter(owaspCategory, boolQuery().filter(termQuery(queryKey, owaspCategory))),
includeCwe,
null)));
- return processSecurityReportSearchResults(request, includeCwe);
+ return search(request, includeCwe);
}
- private List<SecurityStandardCategoryStatistics> processSecurityReportSearchResults(SearchSourceBuilder sourceBuilder, boolean includeCwe) {
+ private List<SecurityStandardCategoryStatistics> search(SearchSourceBuilder sourceBuilder, boolean includeCwe) {
SearchRequest request = EsClient.prepareSearch(TYPE_ISSUE.getMainType())
.source(sourceBuilder);
SearchResponse response = client.search(request);
private final Collection<String> tags;
private final Collection<String> types;
private final Collection<String> owaspTop10;
+ private final Collection<String> owaspTop10For2021;
private final Collection<String> sansTop25;
private final Collection<String> cwe;
private final Collection<String> sonarsourceSecurity;
this.tags = defaultCollection(builder.tags);
this.types = defaultCollection(builder.types);
this.owaspTop10 = defaultCollection(builder.owaspTop10);
+ this.owaspTop10For2021 = defaultCollection(builder.owaspTop10For2021);
this.sansTop25 = defaultCollection(builder.sansTop25);
this.cwe = defaultCollection(builder.cwe);
this.sonarsourceSecurity = defaultCollection(builder.sonarsourceSecurity);
return owaspTop10;
}
+ public Collection<String> owaspTop10For2021() {
+ return owaspTop10For2021;
+ }
+
public Collection<String> sansTop25() {
return sansTop25;
}
private Collection<String> tags;
private Collection<String> types;
private Collection<String> owaspTop10;
+ private Collection<String> owaspTop10For2021;
private Collection<String> sansTop25;
private Collection<String> cwe;
private Collection<String> sonarsourceSecurity;
return this;
}
+ public Builder owaspTop10For2021(@Nullable Collection<String> o) {
+ this.owaspTop10For2021 = o;
+ return this;
+ }
+
public Builder sansTop25(@Nullable Collection<String> s) {
this.sansTop25 = s;
return this;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2017;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.server.issue.IssueDocTesting.newDoc;
import static org.sonar.server.security.SecurityStandards.SANS_TOP_25_INSECURE_INTERACTION;
newDoc("anotherProject", another).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.CRITICAL),
newDoc("openvul1", project).setOwaspTop10(singletonList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
newDoc("notopenvul", project).setOwaspTop10(asList("a1")).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)
.setSeverity(Severity.BLOCKER));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
// Previous vulnerabilities in projects that are not reanalyzed will have no owasp nor cwe attributes (not even 'unknown')
newDoc("openvulNotReindexed", project).setType(RuleType.VULNERABILITY).setStatus(Issue.STATUS_OPEN).setSeverity(Severity.MAJOR));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating)
newDoc("openhotspot1", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW),
newDoc("anotherProject", another).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
.contains(
newDoc("closedHotspot", project).setOwaspTop10(asList("a1")).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FIXED));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, false, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots)
.contains(
.setResolution(Issue.RESOLUTION_FIXED),
newDoc("notowasphotspot", project).setOwaspTop10(singletonList(UNKNOWN_STANDARD)).setType(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW));
- List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe);
+ List<SecurityStandardCategoryStatistics> owaspTop10Report = underTest.getOwaspTop10Report(project.uuid(), false, includeCwe, Y2017);
assertThat(owaspTop10Report)
.extracting(SecurityStandardCategoryStatistics::getCategory, SecurityStandardCategoryStatistics::getVulnerabilities,
SecurityStandardCategoryStatistics::getVulnerabiliyRating, SecurityStandardCategoryStatistics::getToReviewSecurityHotspots,
package org.sonar.server.issue.index;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
import java.util.Date;
+import java.util.List;
import org.junit.Test;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.Severity;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.server.issue.index.IssueQuery.PeriodStart;
-import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
RuleDefinitionDto rule = new RuleDefinitionDto().setUuid(Uuids.createFast());
PeriodStart filterDate = new IssueQuery.PeriodStart(new Date(10_000_000_000L), false);
IssueQuery query = IssueQuery.builder()
- .issueKeys(newArrayList("ABCDE"))
- .severities(newArrayList(Severity.BLOCKER))
- .statuses(Lists.newArrayList(Issue.STATUS_RESOLVED))
- .resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE))
- .projectUuids(newArrayList("PROJECT"))
- .componentUuids(newArrayList("org/struts/Action.java"))
- .moduleUuids(newArrayList("org.struts:core"))
- .rules(newArrayList(rule))
- .assigneeUuids(newArrayList("gargantua"))
- .languages(newArrayList("xoo"))
- .tags(newArrayList("tag1", "tag2"))
- .types(newArrayList("RELIABILITY", "SECURITY"))
- .owaspTop10(newArrayList("a1", "a2"))
- .sansTop25(newArrayList("insecure-interaction", "porous-defenses"))
- .cwe(newArrayList("12", "125"))
+ .issueKeys(List.of("ABCDE"))
+ .severities(List.of(Severity.BLOCKER))
+ .statuses(List.of(Issue.STATUS_RESOLVED))
+ .resolutions(List.of(Issue.RESOLUTION_FALSE_POSITIVE))
+ .projectUuids(List.of("PROJECT"))
+ .componentUuids(List.of("org/struts/Action.java"))
+ .moduleUuids(List.of("org.struts:core"))
+ .rules(List.of(rule))
+ .assigneeUuids(List.of("gargantua"))
+ .languages(List.of("xoo"))
+ .tags(List.of("tag1", "tag2"))
+ .types(List.of("RELIABILITY", "SECURITY"))
+ .owaspTop10(List.of("a1", "a2"))
+ .owaspTop10For2021(List.of("a3", "a4"))
+ .sansTop25(List.of("insecure-interaction", "porous-defenses"))
+ .cwe(List.of("12", "125"))
.branchUuid("my_branch")
.createdAfterByProjectUuids(ImmutableMap.of("PROJECT", filterDate))
.assigned(true)
assertThat(query.tags()).containsOnly("tag1", "tag2");
assertThat(query.types()).containsOnly("RELIABILITY", "SECURITY");
assertThat(query.owaspTop10()).containsOnly("a1", "a2");
+ assertThat(query.owaspTop10For2021()).containsOnly("a3", "a4");
assertThat(query.sansTop25()).containsOnly("insecure-interaction", "porous-defenses");
assertThat(query.cwe()).containsOnly("12", "125");
assertThat(query.branchUuid()).isEqualTo("my_branch");
@Test
public void build_query_without_dates() {
IssueQuery query = IssueQuery.builder()
- .issueKeys(newArrayList("ABCDE"))
+ .issueKeys(List.of("ABCDE"))
.createdAfter(null)
.createdBefore(null)
.createdAt(null)
boolean isExternal();
}
+ enum CweVersion {
+ Y2020("2020"), Y2021("2021");
+
+ private final String label;
+
+ CweVersion(String label) {
+ this.label = label;
+ }
+
+ public String label() {
+ return label;
+ }
+ }
+
enum OwaspTop10Version {
Y2017("2017"), Y2021("2021");
public static final String PARAM_TAGS = "tags";
public static final String PARAM_TYPES = "types";
public static final String PARAM_OWASP_TOP_10 = "owaspTop10";
+ public static final String PARAM_OWASP_TOP_10_2021 = "owaspTop10_2021";
@Deprecated
public static final String PARAM_SANS_TOP_25 = "sansTop25";
public static final String PARAM_CWE_TOP_25 = "cweTop25";