diff options
author | Belen Pruvost <belen.pruvost@sonarsource.com> | 2022-03-21 09:47:47 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-03-21 20:02:53 +0000 |
commit | cd54028b99d846f7d9a0d3494dd8bcbbd998b088 (patch) | |
tree | 74626dc410cdd0472d2c6de68363bd23d7db0547 /server/sonar-server-common/src | |
parent | c361760aa2c86f0beac22d50f4d8ec836a5142ae (diff) | |
download | sonarqube-cd54028b99d846f7d9a0d3494dd8bcbbd998b088.tar.gz sonarqube-cd54028b99d846f7d9a0d3494dd8bcbbd998b088.zip |
SONAR-16130 - 'OWASP Top 10 - 2021' Facet and Filter for rules/search
Diffstat (limited to 'server/sonar-server-common/src')
5 files changed, 95 insertions, 64 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java index a016b98be16..5ce56bdec82 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleDoc.java @@ -168,6 +168,16 @@ public class RuleDoc extends BaseDoc { } @CheckForNull + public Collection<String> getOwaspTop10For2021() { + return getNullableField(RuleIndexDefinition.FIELD_RULE_OWASP_TOP_10_2021); + } + + public RuleDoc setOwaspTop10For2021(@Nullable Collection<String> o) { + setField(RuleIndexDefinition.FIELD_RULE_OWASP_TOP_10_2021, o); + return this; + } + + @CheckForNull public Collection<String> getSansTop25() { return getNullableField(RuleIndexDefinition.FIELD_RULE_SANS_TOP_25); } @@ -290,6 +300,7 @@ public class RuleDoc extends BaseDoc { .setLanguage(dto.getLanguage()) .setCwe(securityStandards.getCwe()) .setOwaspTop10(securityStandards.getOwaspTop10()) + .setOwaspTop10For2021(securityStandards.getOwaspTop10For2021()) .setSansTop25(securityStandards.getSansTop25()) .setSonarSourceSecurityCategory(securityStandards.getSqCategory()) .setName(dto.getName()) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java index 2933b7eacdc..0c2e260eeae 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java @@ -98,6 +98,7 @@ import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_KEY; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_LANGUAGE; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_NAME; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_OWASP_TOP_10; +import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_OWASP_TOP_10_2021; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_REPOSITORY; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_RULE_KEY; import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_SANS_TOP_25; @@ -128,6 +129,7 @@ public class RuleIndex { public static final String FACET_CWE = "cwe"; public static final String FACET_SANS_TOP_25 = "sansTop25"; public static final String FACET_OWASP_TOP_10 = "owaspTop10"; + public static final String FACET_OWASP_TOP_10_2021 = "owaspTop10-2021"; public static final String FACET_SONARSOURCE_SECURITY = "sonarsourceSecurity"; private static final int MAX_FACET_SIZE = 100; @@ -215,13 +217,13 @@ public class RuleIndex { BoolQueryBuilder textQuery = boolQuery(); JavaTokenizer.split(queryText) .stream().map(token -> boolQuery().should( - matchQuery( - SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME), - StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F)) - .should( - matchPhraseQuery( - ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION), - token).boost(3F))) + matchQuery( + SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME), + StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F)) + .should( + matchPhraseQuery( + ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION), + token).boost(3F))) .forEach(textQuery::must); qb.should(textQuery.boost(20F)); @@ -235,7 +237,7 @@ public class RuleIndex { private static QueryBuilder termQuery(String field, String query, float boost) { return QueryBuilders.multiMatchQuery(query, - field, SEARCH_WORDS_ANALYZER.subField(field)) + field, SEARCH_WORDS_ANALYZER.subField(field)) .operator(Operator.AND) .boost(boost); } @@ -255,63 +257,27 @@ public class RuleIndex { QueryBuilders.termQuery(FIELD_RULE_STATUS, RuleStatus.REMOVED.toString()))); - if (StringUtils.isNotEmpty(query.getInternalKey())) { - filters.put(FIELD_RULE_INTERNAL_KEY, - QueryBuilders.termQuery(FIELD_RULE_INTERNAL_KEY, query.getInternalKey())); - } + addFilter(filters, FIELD_RULE_INTERNAL_KEY, query.getInternalKey()); - if (StringUtils.isNotEmpty(query.getRuleKey())) { - filters.put(FIELD_RULE_RULE_KEY, - QueryBuilders.termQuery(FIELD_RULE_RULE_KEY, query.getRuleKey())); - } + addFilter(filters, FIELD_RULE_RULE_KEY, query.getRuleKey()); - if (isNotEmpty(query.getLanguages())) { - filters.put(FIELD_RULE_LANGUAGE, - QueryBuilders.termsQuery(FIELD_RULE_LANGUAGE, query.getLanguages())); - } + addFilter(filters, query.getLanguages(), FIELD_RULE_LANGUAGE); - if (isNotEmpty(query.getRepositories())) { - filters.put(FIELD_RULE_REPOSITORY, - QueryBuilders.termsQuery(FIELD_RULE_REPOSITORY, query.getRepositories())); - } + addFilter(filters, query.getRepositories(), FIELD_RULE_REPOSITORY); - if (isNotEmpty(query.getSeverities())) { - filters.put(FIELD_RULE_SEVERITY, - QueryBuilders.termsQuery(FIELD_RULE_SEVERITY, query.getSeverities())); - } + addFilter(filters, query.getSeverities(), FIELD_RULE_SEVERITY); - if (isNotEmpty(query.getCwe())) { - filters.put(FIELD_RULE_CWE, - boolQuery() - .must(QueryBuilders.termsQuery(FIELD_RULE_CWE, query.getCwe())) - .must(QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))); - } + addSecurityStandardFilter(filters, FIELD_RULE_CWE, query.getCwe()); - if (isNotEmpty(query.getOwaspTop10())) { - filters.put(FIELD_RULE_OWASP_TOP_10, - boolQuery() - .must(QueryBuilders.termsQuery(FIELD_RULE_OWASP_TOP_10, query.getOwaspTop10())) - .must(QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))); - } + addSecurityStandardFilter(filters, FIELD_RULE_OWASP_TOP_10, query.getOwaspTop10()); - if (isNotEmpty(query.getSansTop25())) { - filters.put(FIELD_RULE_SANS_TOP_25, - boolQuery() - .must(QueryBuilders.termsQuery(FIELD_RULE_SANS_TOP_25, query.getSansTop25())) - .must(QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))); - } + addSecurityStandardFilter(filters, FIELD_RULE_OWASP_TOP_10_2021, query.getOwaspTop10For2021()); - if (isNotEmpty(query.getSonarsourceSecurity())) { - filters.put(FIELD_RULE_SONARSOURCE_SECURITY, - boolQuery() - .must(QueryBuilders.termsQuery(FIELD_RULE_SONARSOURCE_SECURITY, query.getSonarsourceSecurity())) - .must(QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))); - } + addSecurityStandardFilter(filters, FIELD_RULE_SANS_TOP_25, query.getSansTop25()); - if (StringUtils.isNotEmpty(query.getKey())) { - filters.put(FIELD_RULE_KEY, - QueryBuilders.termQuery(FIELD_RULE_KEY, query.getKey())); - } + addSecurityStandardFilter(filters, FIELD_RULE_SONARSOURCE_SECURITY, query.getSonarsourceSecurity()); + + addFilter(filters, FIELD_RULE_KEY, query.getKey()); if (isNotEmpty(query.getTags())) { filters.put(FIELD_RULE_TAGS, buildTagsFilter(query.getTags())); @@ -384,6 +350,29 @@ public class RuleIndex { return filters; } + private static void addSecurityStandardFilter(Map<String, QueryBuilder> filters, String key, Collection<String> values) { + if (isNotEmpty(values)) { + filters.put(key, + boolQuery() + .must(QueryBuilders.termsQuery(key, values)) + .must(QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))); + } + } + + private static void addFilter(Map<String, QueryBuilder> filters, Collection<String> key, String value) { + if (isNotEmpty(key)) { + filters.put(value, + QueryBuilders.termsQuery(value, key)); + } + } + + private static void addFilter(Map<String, QueryBuilder> filters, String key, String value) { + if (StringUtils.isNotEmpty(value)) { + filters.put(key, + QueryBuilders.termQuery(key, value)); + } + } + private static BoolQueryBuilder buildTagsFilter(Collection<String> tags) { BoolQueryBuilder q = boolQuery(); for (String tag : tags) { @@ -476,10 +465,10 @@ public class RuleIndex { private static Function<TermsAggregationBuilder, AggregationBuilder> filterSecurityCategories() { return termsAggregation -> AggregationBuilders.filter( - "filter_by_rule_types_" + termsAggregation.getName(), - termsQuery(FIELD_RULE_TYPE, - VULNERABILITY.name(), - SECURITY_HOTSPOT.name())) + "filter_by_rule_types_" + termsAggregation.getName(), + termsQuery(FIELD_RULE_TYPE, + VULNERABILITY.name(), + SECURITY_HOTSPOT.name())) .subAggregation(termsAggregation); } @@ -499,6 +488,13 @@ public class RuleIndex { FACET_DEFAULT_SIZE, filterSecurityCategories(), (categories == null) ? (new String[0]) : categories.toArray())); } + if (options.getFacets().contains(FACET_OWASP_TOP_10_2021)) { + Collection<String> categories = query.getOwaspTop10For2021(); + aggregations.put(FACET_OWASP_TOP_10_2021, + stickyFacetBuilder.buildStickyFacet(FIELD_RULE_OWASP_TOP_10_2021, FACET_OWASP_TOP_10_2021, + FACET_DEFAULT_SIZE, filterSecurityCategories(), + (categories == null) ? (new String[0]) : categories.toArray())); + } if (options.getFacets().contains(FACET_SANS_TOP_25)) { Collection<String> categories = query.getSansTop25(); aggregations.put(FACET_SANS_TOP_25, diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java index 23b0b2a2693..63b5330cbc6 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java @@ -21,6 +21,7 @@ package org.sonar.server.rule.index; import com.google.common.collect.ImmutableSet; import java.util.Set; +import javax.inject.Inject; import org.sonar.api.config.Configuration; import org.sonar.api.config.internal.MapSettings; import org.sonar.server.es.Index; @@ -37,8 +38,6 @@ import static org.sonar.server.es.newindex.DefaultIndexSettingsElement.SORTABLE_ import static org.sonar.server.es.newindex.SettingsConfiguration.MANUAL_REFRESH_INTERVAL; import static org.sonar.server.es.newindex.SettingsConfiguration.newBuilder; -import javax.inject.Inject; - /** * Definition of ES index "rules", including settings and fields. */ @@ -64,6 +63,7 @@ public class RuleIndexDefinition implements IndexDefinition { public static final String FIELD_RULE_UPDATED_AT = "updatedAt"; public static final String FIELD_RULE_CWE = "cwe"; public static final String FIELD_RULE_OWASP_TOP_10 = "owaspTop10"; + public static final String FIELD_RULE_OWASP_TOP_10_2021 = "owaspTop10-2021"; public static final String FIELD_RULE_SANS_TOP_25 = "sansTop25"; public static final String FIELD_RULE_SONARSOURCE_SECURITY = "sonarsourceSecurity"; public static final String FIELD_RULE_TAGS = "tags"; @@ -146,6 +146,7 @@ public class RuleIndexDefinition implements IndexDefinition { ruleMapping.keywordFieldBuilder(FIELD_RULE_CWE).disableNorms().build(); ruleMapping.keywordFieldBuilder(FIELD_RULE_OWASP_TOP_10).disableNorms().build(); + ruleMapping.keywordFieldBuilder(FIELD_RULE_OWASP_TOP_10_2021).disableNorms().build(); ruleMapping.keywordFieldBuilder(FIELD_RULE_SANS_TOP_25).disableNorms().build(); ruleMapping.keywordFieldBuilder(FIELD_RULE_SONARSOURCE_SECURITY).disableNorms().build(); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleQuery.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleQuery.java index 57867a732eb..54c9116d62d 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleQuery.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleQuery.java @@ -54,6 +54,7 @@ public class RuleQuery { private String ruleKey; private boolean includeExternal; private Collection<String> owaspTop10; + private Collection<String> owaspTop10For2021; private Collection<String> sansTop25; private Collection<String> cwe; private Collection<String> sonarsourceSecurity; @@ -308,6 +309,16 @@ public class RuleQuery { } @CheckForNull + public Collection<String> getOwaspTop10For2021() { + return owaspTop10For2021; + } + + public RuleQuery setOwaspTop10For2021(@Nullable Collection<String> owaspTop10For2021) { + this.owaspTop10For2021 = owaspTop10For2021; + return this; + } + + @CheckForNull public Collection<String> getSansTop25() { return sansTop25; } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java index 511ec5c6b54..8f367c1069d 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java @@ -458,7 +458,7 @@ public class RuleIndexTest { } @Test - public void search_by_security_owaspTop10_return_vulnerabilities_and_hotspots_only() { + public void search_by_security_owaspTop10_2017_return_vulnerabilities_and_hotspots_only() { RuleDefinitionDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:543")), r -> r.setType(VULNERABILITY)); RuleDefinitionDto rule2 = createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT)); createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL)); @@ -470,6 +470,18 @@ public class RuleIndexTest { } @Test + public void search_by_security_owaspTop10_2021_return_vulnerabilities_and_hotspots_only() { + RuleDefinitionDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")), r -> r.setType(VULNERABILITY)); + RuleDefinitionDto rule2 = createRule(setSecurityStandards(of("owaspTop10-2021:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT)); + createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL)); + index(); + + RuleQuery query = new RuleQuery().setOwaspTop10For2021(of("a5", "a10")); + SearchIdResult<String> results = underTest.search(query, new SearchOptions().addFacets("owaspTop10-2021")); + assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule2.getUuid()); + } + + @Test public void search_by_security_sansTop25_return_vulnerabilities_and_hotspots_only() { RuleDefinitionDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:89")), r -> r.setType(VULNERABILITY)); RuleDefinitionDto rule2 = createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:829")), r -> r.setType(SECURITY_HOTSPOT)); @@ -483,7 +495,7 @@ public class RuleIndexTest { @Test public void search_by_security_sonarsource_return_vulnerabilities_and_hotspots_only() { - RuleDefinitionDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:89")), r -> r.setType(VULNERABILITY)); + RuleDefinitionDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10-2021:a10", "cwe:89")), r -> r.setType(VULNERABILITY)); createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:829")), r -> r.setType(CODE_SMELL)); RuleDefinitionDto rule3 = createRule(setSecurityStandards(of("cwe:601")), r -> r.setType(SECURITY_HOTSPOT)); index(); |