aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common
diff options
context:
space:
mode:
authorDejan Milisavljevic <dejan.milisavljevic@sonarsource.com>2024-11-21 08:05:01 +0100
committersonartech <sonartech@sonarsource.com>2024-11-21 20:02:47 +0000
commit865b3112294654f3f3bbf1fd25d7520a397bbf30 (patch)
tree061773bb8be848033f2487ceb0f177a30c2410d1 /server/sonar-server-common
parentfbb315f1aafc302a512ea3b70f06c6dac3a4e3b9 (diff)
downloadsonarqube-865b3112294654f3f3bbf1fd25d7520a397bbf30.tar.gz
sonarqube-865b3112294654f3f3bbf1fd25d7520a397bbf30.zip
SONAR-23341 Security report data are based on Security impact when in MQR mode
Diffstat (limited to 'server/sonar-server-common')
-rw-r--r--server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java225
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/rule/index/RuleIndex.java52
2 files changed, 179 insertions, 98 deletions
diff --git a/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java b/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java
index 87c8474ffe6..17865560586 100644
--- a/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java
+++ b/server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java
@@ -25,10 +25,14 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.sonar.api.config.Configuration;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality;
@@ -60,6 +64,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.CRITICAL;
import static org.sonar.api.rule.Severity.INFO;
@@ -69,6 +75,7 @@ import static org.sonar.api.rules.RuleType.BUG;
import static org.sonar.api.rules.RuleType.CODE_SMELL;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.core.config.MQRModeConstants.MULTI_QUALITY_MODE_ENABLED;
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
import static org.sonar.db.rule.RuleTesting.newRule;
import static org.sonar.db.rule.RuleTesting.setCleanCodeAttribute;
@@ -99,23 +106,24 @@ import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
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 RuleIndexIT {
+class RuleIndexIT {
private final System2 system2 = new AlwaysIncreasingSystem2();
- @Rule
- public EsTester es = EsTester.create();
- @Rule
- public DbTester db = DbTester.create(system2);
+ @RegisterExtension
+ private final EsTester es = EsTester.create();
+ @RegisterExtension
+ private final DbTester db = DbTester.create(system2);
private final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
private final ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
+ private final Configuration config = mock(Configuration.class);
- private final RuleIndex underTest = new RuleIndex(es.client(), system2);
+ private final RuleIndex underTest = new RuleIndex(es.client(), system2, config);
private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
@Test
- public void search_all_rules() {
+ void search_all_rules() {
createRule();
createRule();
index();
@@ -127,7 +135,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_key() {
+ void search_by_key() {
RuleDto js1 = createRule(
setRepositoryKey("javascript"),
setRuleKey("X001"));
@@ -153,7 +161,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_case_insensitive_key() {
+ void search_by_case_insensitive_key() {
RuleDto ruleDto = createRule(
setRepositoryKey("javascript"),
setRuleKey("X001"));
@@ -164,7 +172,7 @@ public class RuleIndexIT {
}
@Test
- public void filter_by_key() {
+ void filter_by_key() {
createRule(
setRepositoryKey("javascript"),
setRuleKey("X001"));
@@ -187,7 +195,7 @@ public class RuleIndexIT {
}
@Test
- public void search_name_by_query() {
+ void search_name_by_query() {
createRule(setName("testing the partial match and matching of rule"));
index();
@@ -209,7 +217,7 @@ public class RuleIndexIT {
}
@Test
- public void search_name_with_protected_chars() {
+ void search_name_with_protected_chars() {
RuleDto rule = createRule(setName("ja#va&sc\"r:ipt"));
index();
@@ -219,7 +227,7 @@ public class RuleIndexIT {
}
@Test
- public void search_content_by_query() {
+ void search_content_by_query() {
// it's important to set all the fields being used by the search (name, desc, key, lang, ...),
// otherwise the generated random values may raise false-positives
RuleDto rule1 = insertJavaRule("My great rule CWE-123 which makes your code 1000 times better!", "123", "rule 123");
@@ -276,7 +284,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_any_of_repositories() {
+ void search_by_any_of_repositories() {
RuleDto findbugs = createRule(
setRepositoryKey("findbugs"),
setRuleKey("S001"));
@@ -299,7 +307,7 @@ public class RuleIndexIT {
}
@Test
- public void filter_by_tags() {
+ void filter_by_tags() {
RuleDto rule1 = createRule(setSystemTags("tag1s"), setTags("tag1"));
RuleDto rule2 = createRule(setSystemTags("tag2s"), setTags("tag2"));
index();
@@ -323,7 +331,7 @@ public class RuleIndexIT {
}
@Test
- public void tags_facet_supports_selected_value_with_regexp_special_characters() {
+ void tags_facet_supports_selected_value_with_regexp_special_characters() {
createRule(r -> r.setTags(of("misra++")));
index();
@@ -336,7 +344,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_types() {
+ void search_by_types() {
createRule(setType(CODE_SMELL));
RuleDto vulnerability = createRule(setType(VULNERABILITY));
RuleDto bug1 = createRule(setType(BUG));
@@ -368,7 +376,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_is_template() {
+ void search_by_is_template() {
RuleDto ruleNoTemplate = createRule(setIsTemplate(false));
RuleDto ruleIsTemplate = createRule(setIsTemplate(true));
index();
@@ -395,7 +403,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_is_external() {
+ void search_by_is_external() {
RuleDto ruleIsNotExternal = createRule(setIsExternal(false));
RuleDto ruleIsExternal = createRule(setIsExternal(true));
index();
@@ -412,7 +420,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_template_key() {
+ void search_by_template_key() {
RuleDto template = createRule(setIsTemplate(true));
RuleDto customRule = createRule(setTemplateId(template.getUuid()));
index();
@@ -433,7 +441,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_any_of_languages() {
+ void search_by_any_of_languages() {
createRule(setLanguage("java"));
RuleDto javascript = createRule(setLanguage("js"));
index();
@@ -455,11 +463,15 @@ public class RuleIndexIT {
assertThat(underTest.search(query, new SearchOptions()).getUuids()).hasSize(2);
}
- @Test
- public void search_by_security_cwe_return_vulnerabilities_and_hotspots_only() {
- RuleDto rule1 = createRule(setSecurityStandards(of("cwe:543", "cwe:123", "owaspTop10:a1")), r -> r.setType(VULNERABILITY));
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_cwe_return_correct_data_based_on_mode(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ RuleDto rule1 = createRule(setSecurityStandards(of("cwe:543", "cwe:123", "owaspTop10:a1")),
+ r -> r.setType(VULNERABILITY).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
RuleDto rule2 = createRule(setSecurityStandards(of("cwe:543", "owaspTop10:a1")), r -> r.setType(SECURITY_HOTSPOT));
- createRule(setSecurityStandards(of("owaspTop10:a1")), r -> r.setType(CODE_SMELL));
+ createRule(setSecurityStandards(of("owaspTop10:a1")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
index();
RuleQuery query = new RuleQuery().setCwe(of("543"));
@@ -467,11 +479,15 @@ public class RuleIndexIT {
assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule2.getUuid());
}
- @Test
- public void search_by_security_owaspTop10_2017_return_vulnerabilities_and_hotspots_only() {
- RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:543")), r -> r.setType(VULNERABILITY));
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_owaspTop10_2017_return_correct_data_based_on_mode(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:543")),
+ r -> r.setType(VULNERABILITY).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT));
- createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL));
+ createRule(setSecurityStandards(of("cwe:543")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
index();
RuleQuery query = new RuleQuery().setOwaspTop10(of("a5", "a10"));
@@ -479,12 +495,15 @@ public class RuleIndexIT {
assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule2.getUuid());
}
- @Test
- public void search_by_security_owaspTop10_2021_return_vulnerabilities_and_hotspots_only() {
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_owaspTop10_2021_return_correct_data_based_on_mode(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")),
- r -> r.setType(VULNERABILITY));
+ r -> r.setType(VULNERABILITY).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10-2021:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT));
- createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL));
+ createRule(setSecurityStandards(of("cwe:543")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
index();
RuleQuery query = new RuleQuery().setOwaspTop10For2021(of("a5", "a10"));
@@ -492,11 +511,15 @@ public class RuleIndexIT {
assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule2.getUuid());
}
- @Test
- public void search_by_security_sansTop25_return_vulnerabilities_and_hotspots_only() {
- RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:89")), r -> r.setType(VULNERABILITY));
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_sansTop25_return_correct_data_based_on_mode(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10:a10", "cwe:89")),
+ r -> r.setType(VULNERABILITY).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:829")), r -> r.setType(SECURITY_HOTSPOT));
- createRule(setSecurityStandards(of("cwe:306")), r -> r.setType(CODE_SMELL));
+ createRule(setSecurityStandards(of("cwe:306")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
index();
RuleQuery query = new RuleQuery().setSansTop25(of(SANS_TOP_25_INSECURE_INTERACTION, SANS_TOP_25_RISKY_RESOURCE));
@@ -504,10 +527,14 @@ public class RuleIndexIT {
assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule2.getUuid());
}
- @Test
- public void search_by_security_sonarsource_return_vulnerabilities_and_hotspots_only() {
- RuleDto 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));
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_sonarsource_return_correct_data_based_on_mode(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10-2021:a10", "cwe:89")),
+ r -> r.setType(VULNERABILITY).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
+ createRule(setSecurityStandards(of("owaspTop10:a10", "cwe:829")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
RuleDto rule3 = createRule(setSecurityStandards(of("cwe:601")), r -> r.setType(SECURITY_HOTSPOT));
index();
@@ -517,8 +544,34 @@ public class RuleIndexIT {
}
@Test
- public void search_by_security_sonarsource_return_complete_list_of_facets() {
+ void search_by_security_should_count_issues_based_on_the_mode() {
+
+ //Should not be counted in MQR mode as it has no security impact
+ RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10-2021:a10", "cwe:89")), r -> r.setType(VULNERABILITY));
+
+ //Should not be counted in Standard mode because it is not a Vulnerability type
+ RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10:a1", "owaspTop10-2021:a10", "cwe:89")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.SECURITY, Severity.HIGH))));
+
+ //Hotspots should be counted in both modes
+ RuleDto rule3 = createRule(setSecurityStandards(of("cwe:601")), r -> r.setType(SECURITY_HOTSPOT));
+ index();
+
+ RuleQuery query = new RuleQuery().setSonarsourceSecurity(of("sql-injection", "open-redirect"));
+
+ doReturn(Optional.of(true)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ SearchIdResult<String> results = underTest.search(query, new SearchOptions().addFacets("sonarsourceSecurity"));
+ assertThat(results.getUuids()).containsOnly(rule2.getUuid(), rule3.getUuid());
+ doReturn(Optional.of(false)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
+ results = underTest.search(query, new SearchOptions().addFacets("sonarsourceSecurity"));
+ assertThat(results.getUuids()).containsOnly(rule1.getUuid(), rule3.getUuid());
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void search_by_security_sonarsource_return_complete_list_of_facets(boolean mqrMode) {
+ doReturn(Optional.of(mqrMode)).when(config).getBoolean(MULTI_QUALITY_MODE_ENABLED);
List<RuleDto> rules = new ArrayList<>();
// Creation of one rule for each standard security category defined (except other)
@@ -526,6 +579,10 @@ public class RuleIndexIT {
rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())),
r -> r.setType(SECURITY_HOTSPOT)));
}
+
+ // Should be ignore because it is not a hotspot, and not a vulnerability (in Standard mode) and not having Security impact (in MQR mode)
+ createRule(setSecurityStandards(of("cwe:787")),
+ r -> r.setType(CODE_SMELL).replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))));
index();
RuleQuery query = new RuleQuery();
@@ -537,7 +594,7 @@ public class RuleIndexIT {
}
@Test
- public void compare_to_another_profile() {
+ void compare_to_another_profile() {
String xoo = "xoo";
QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(xoo));
QProfileDto anotherProfile = db.qualityProfiles().insert(p -> p.setLanguage(xoo));
@@ -574,7 +631,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_any_of_severities() {
+ void search_by_any_of_severities() {
createRule(setSeverity(BLOCKER));
RuleDto info = createRule(setSeverity(INFO));
index();
@@ -597,7 +654,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_any_of_statuses() {
+ void search_by_any_of_statuses() {
createRule(setStatus(RuleStatus.BETA));
RuleDto ready = createRule(setStatus(RuleStatus.READY));
index();
@@ -620,7 +677,7 @@ public class RuleIndexIT {
}
@Test
- public void activation_parameter_is_ignored_if_profile_is_not_set() {
+ void activation_parameter_is_ignored_if_profile_is_not_set() {
RuleDto rule1 = createJavaRule();
RuleDto rule2 = createJavaRule();
QProfileDto profile1 = createJavaProfile();
@@ -632,7 +689,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_activation() {
+ void search_by_activation() {
RuleDto rule1 = createJavaRule();
RuleDto rule2 = createJavaRule();
RuleDto rule3 = createJavaRule();
@@ -679,7 +736,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_activation_and_inheritance() {
+ void search_by_activation_and_inheritance() {
RuleDto rule1 = createJavaRule();
RuleDto rule2 = createJavaRule();
RuleDto rule3 = createJavaRule();
@@ -714,7 +771,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_activation_and_severity() {
+ void search_by_activation_and_severity() {
RuleDto major = createRule(setSeverity(MAJOR));
RuleDto minor = createRule(setSeverity(MINOR));
createRule(setSeverity(INFO));
@@ -737,7 +794,7 @@ public class RuleIndexIT {
}
@Test
- public void facet_by_activation_severity_is_ignored_when_profile_is_not_specified() {
+ void facet_by_activation_severity_is_ignored_when_profile_is_not_specified() {
RuleDto rule = createJavaRule();
QProfileDto profile = createJavaProfile();
db.qualityProfiles().activateRule(profile, rule);
@@ -759,7 +816,7 @@ public class RuleIndexIT {
}
@Test
- public void listTags_should_return_tags() {
+ void listTags_should_return_tags() {
createRule(setSystemTags("sys1", "sys2"), setTags("tag1"));
createRule(setSystemTags(), setTags("tag2"));
@@ -769,14 +826,14 @@ public class RuleIndexIT {
}
@Test
- public void fail_to_list_tags_when_size_greater_than_500() {
+ void fail_to_list_tags_when_size_greater_than_500() {
assertThatThrownBy(() -> underTest.listTags(null, 501))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Page size must be lower than or equals to 500");
}
@Test
- public void available_since() {
+ void available_since() {
RuleDto ruleOld = createRule(setCreatedAt(-2_000L));
RuleDto ruleOlder = createRule(setCreatedAt(-1_000L));
index();
@@ -794,7 +851,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_clean_code_attribute() {
+ void search_by_clean_code_attribute() {
RuleDto ruleDto = createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.FOCUSED));
index();
@@ -812,7 +869,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_software_quality() {
+ void search_by_software_quality() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
index();
@@ -829,7 +886,7 @@ public class RuleIndexIT {
}
@Test
- public void search_by_severity() {
+ void search_by_severity() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER);
RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
index();
@@ -844,7 +901,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_clean_code_attribute_category_facet() {
+ void search_should_support_clean_code_attribute_category_facet() {
createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.FOCUSED));
createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.LOGICAL));
index();
@@ -859,7 +916,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_clean_code_attribute_category_facet_with_filtering() {
+ void search_should_support_clean_code_attribute_category_facet_with_filtering() {
RuleDto php = createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.FOCUSED));
RuleDto php1 = createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.LOGICAL));
RuleDto java = createRule(setRepositoryKey("java"), setCleanCodeAttribute(CleanCodeAttribute.CONVENTIONAL));
@@ -876,7 +933,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_software_quality_facet() {
+ void search_should_support_software_quality_facet() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
@@ -896,7 +953,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_software_quality_facet_with_filtering() {
+ void search_should_support_software_quality_facet_with_filtering() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
@@ -917,7 +974,7 @@ public class RuleIndexIT {
}
@Test
- public void search_whenFilteringOnSeverityAndSoftwareQuality_shouldReturnFacet() {
+ void search_whenFilteringOnSeverityAndSoftwareQuality_shouldReturnFacet() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.LOW);
@@ -955,7 +1012,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_severity_facet() {
+ void search_should_support_severity_facet() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.BLOCKER);
@@ -978,7 +1035,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_severity_facet_with_filters() {
+ void search_should_support_severity_facet_with_filters() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.INFO);
@@ -1005,7 +1062,7 @@ public class RuleIndexIT {
}
@Test
- public void search_should_support_software_quality_and_severity_facets_with_filtering() {
+ void search_should_support_software_quality_and_severity_facets_with_filtering() {
ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER);
@@ -1034,7 +1091,7 @@ public class RuleIndexIT {
}
@Test
- public void global_facet_on_repositories_and_tags() {
+ void global_facet_on_repositories_and_tags() {
createRule(setRepositoryKey("php"), setSystemTags("sysTag"), setTags());
createRule(setRepositoryKey("php"), setSystemTags(), setTags("tag1"));
createRule(setRepositoryKey("javascript"), setSystemTags(), setTags("tag1", "tag2"));
@@ -1082,7 +1139,7 @@ public class RuleIndexIT {
}
@Test
- public void sticky_facets_base() {
+ void sticky_facets_base() {
setupStickyFacets();
RuleQuery query = new RuleQuery();
@@ -1094,7 +1151,7 @@ public class RuleIndexIT {
* Facet with no filters at all
*/
@Test
- public void sticky_facets_no_filters() {
+ void sticky_facets_no_filters() {
setupStickyFacets();
RuleQuery query = new RuleQuery();
@@ -1113,7 +1170,7 @@ public class RuleIndexIT {
* -- lang facet should still have all language
*/
@Test
- public void sticky_facets_with_1_filter() {
+ void sticky_facets_with_1_filter() {
setupStickyFacets();
RuleQuery query = new RuleQuery().setLanguages(ImmutableList.of("cpp"));
@@ -1127,7 +1184,7 @@ public class RuleIndexIT {
}
@Test
- public void languages_facet_should_return_top_100_items() {
+ void languages_facet_should_return_top_100_items() {
rangeClosed(1, 101).forEach(i -> db.rules().insert(r -> r.setLanguage("lang" + i)));
index();
@@ -1137,7 +1194,7 @@ public class RuleIndexIT {
}
@Test
- public void repositories_facet_should_return_top_100_items() {
+ void repositories_facet_should_return_top_100_items() {
rangeClosed(1, 101).forEach(i -> db.rules().insert(r -> r.setRepositoryKey("repo" + i)));
index();
@@ -1147,7 +1204,7 @@ public class RuleIndexIT {
}
@Test
- public void tags_facet_should_find_tags() {
+ void tags_facet_should_find_tags() {
createRule(setSystemTags(), setTags("bla"));
index();
@@ -1159,7 +1216,7 @@ public class RuleIndexIT {
}
@Test
- public void tags_facet_should_return_top_100_items() {
+ void tags_facet_should_return_top_100_items() {
// default number of items returned in tag facet = 100
String[] tags = get101Tags();
createRule(setSystemTags(tags));
@@ -1174,7 +1231,7 @@ public class RuleIndexIT {
}
@Test
- public void tags_facet_should_include_matching_selected_items() {
+ void tags_facet_should_include_matching_selected_items() {
// default number of items returned in tag facet = 100
String[] tags = get101Tags();
createRule(setSystemTags(tags));
@@ -1194,7 +1251,7 @@ public class RuleIndexIT {
}
@Test
- public void tags_facet_should_be_available() {
+ void tags_facet_should_be_available() {
RuleQuery query = new RuleQuery();
SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
@@ -1209,7 +1266,7 @@ public class RuleIndexIT {
* -- repository for cpp & T2
*/
@Test
- public void sticky_facets_with_2_filters() {
+ void sticky_facets_with_2_filters() {
setupStickyFacets();
RuleQuery query = new RuleQuery()
@@ -1234,7 +1291,7 @@ public class RuleIndexIT {
* -- type
*/
@Test
- public void sticky_facets_with_3_filters() {
+ void sticky_facets_with_3_filters() {
setupStickyFacets();
RuleQuery query = new RuleQuery()
@@ -1254,7 +1311,7 @@ public class RuleIndexIT {
}
@Test
- public void sort_by_name() {
+ void sort_by_name() {
RuleDto abcd = createRule(setName("abcd"));
RuleDto abc = createRule(setName("ABC"));
RuleDto fgh = createRule(setName("FGH"));
@@ -1272,7 +1329,7 @@ public class RuleIndexIT {
}
@Test
- public void default_sort_is_by_updated_at_desc() {
+ void default_sort_is_by_updated_at_desc() {
long currentTimeMillis = System.currentTimeMillis();
RuleDto old = createRule(setCreatedAt(1000L), setUpdatedAt(currentTimeMillis + 1000L));
@@ -1285,7 +1342,7 @@ public class RuleIndexIT {
}
@Test
- public void fail_sort_by_language() {
+ void fail_sort_by_language() {
try {
// Sorting on a field not tagged as sortable
new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_LANGUAGE);
@@ -1296,7 +1353,7 @@ public class RuleIndexIT {
}
@Test
- public void paging() {
+ void paging() {
createRule();
createRule();
createRule();
@@ -1323,7 +1380,7 @@ public class RuleIndexIT {
}
@Test
- public void search_all_keys_by_query() {
+ void search_all_keys_by_query() {
createRule(setRepositoryKey("javascript"), setRuleKey("X001"));
createRule(setRepositoryKey("cobol"), setRuleKey("X001"));
createRule(setRepositoryKey("php"), setRuleKey("S002"));
@@ -1340,7 +1397,7 @@ public class RuleIndexIT {
}
@Test
- public void searchAll_keys_by_profile() {
+ void searchAll_keys_by_profile() {
RuleDto rule1 = createRule();
RuleDto rule2 = createRule();
RuleDto rule3 = createRule();
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 c3b6f438e97..872fe7a69a9 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
@@ -53,6 +53,7 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
+import org.sonar.api.config.Configuration;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
@@ -83,6 +84,8 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.filters;
import static org.elasticsearch.search.aggregations.AggregationBuilders.reverseNested;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.core.config.MQRModeConstants.MULTI_QUALITY_MODE_DEFAULT_VALUE;
+import static org.sonar.core.config.MQRModeConstants.MULTI_QUALITY_MODE_ENABLED;
import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES;
import static org.sonar.server.es.EsUtils.optimizeScrollRequest;
import static org.sonar.server.es.EsUtils.scrollIds;
@@ -155,6 +158,14 @@ public class RuleIndex {
public static final String FACET_IMPACT_SOFTWARE_QUALITY = "impactSoftwareQualities";
public static final String FACET_IMPACT_SEVERITY = "impactSeverities";
+ private static final BoolQueryBuilder SECURITY_IMPACT_AND_HOTSPOT_FILTER =
+ boolQuery()
+ .should(
+ nestedQuery(FIELD_RULE_IMPACTS, termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, SoftwareQuality.SECURITY.name()), ScoreMode.Avg))
+ .should(termsQuery(FIELD_RULE_TYPE, SECURITY_HOTSPOT.name()))
+ .minimumShouldMatch(1);
+
+
private static final int MAX_FACET_SIZE = 100;
public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = Arrays.stream(RuleStatus.values())
@@ -166,10 +177,13 @@ public class RuleIndex {
private final EsClient client;
private final System2 system2;
+ private final Configuration config;
- public RuleIndex(EsClient client, System2 system2) {
+
+ public RuleIndex(EsClient client, System2 system2, Configuration config) {
this.client = client;
this.system2 = system2;
+ this.config = config;
}
public SearchIdResult<String> search(RuleQuery query, SearchOptions options) {
@@ -266,7 +280,7 @@ public class RuleIndex {
}
/* Build main filter (match based) */
- private static Map<String, QueryBuilder> buildFilters(RuleQuery query) {
+ private Map<String, QueryBuilder> buildFilters(RuleQuery query) {
Map<String, QueryBuilder> filters = new HashMap<>();
/* Add enforced filter on main type Rule */
@@ -407,12 +421,14 @@ public class RuleIndex {
allFilters.put(FIELD_RULE_IMPACTS, nestedQuery(FIELD_ISSUE_IMPACTS, impactsFilter, ScoreMode.Avg));
}
- private static void addSecurityStandardFilter(Map<String, QueryBuilder> filters, String key, Collection<String> values) {
+ private 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())));
+ .must(isMQRMode() ?
+ SECURITY_IMPACT_AND_HOTSPOT_FILTER :
+ QueryBuilders.termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name())));
}
}
@@ -482,7 +498,7 @@ public class RuleIndex {
return filter;
}
- private static Map<String, AggregationBuilder> getFacets(RuleQuery query, SearchOptions options, QueryBuilder queryBuilder,
+ private Map<String, AggregationBuilder> getFacets(RuleQuery query, SearchOptions options, QueryBuilder queryBuilder,
Map<String, QueryBuilder> filters) {
Map<String, AggregationBuilder> aggregations = new HashMap<>();
StickyFacetBuilder stickyFacetBuilder = stickyFacetBuilder(queryBuilder, filters);
@@ -500,7 +516,7 @@ public class RuleIndex {
return aggregations;
}
- private static void addDefaultFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations,
+ private void addDefaultFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations,
StickyFacetBuilder stickyFacetBuilder) {
if (options.getFacets().contains(FACET_LANGUAGES) || options.getFacets().contains(FACET_OLD_DEFAULT)) {
Collection<String> languages = query.getLanguages();
@@ -600,16 +616,20 @@ public class RuleIndex {
return boolQueryBuilder;
}
- 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()))
- .subAggregation(termsAggregation);
+ private Function<TermsAggregationBuilder, AggregationBuilder> filterSecurityCategories() {
+ if (isMQRMode()) {
+ return termsAggregation -> AggregationBuilders.filter("filter_by_security_impact_" + termsAggregation.getName(),
+ SECURITY_IMPACT_AND_HOTSPOT_FILTER).subAggregation(termsAggregation);
+
+ } else {
+ return termsAggregation -> AggregationBuilders.filter(
+ "filter_by_rule_types_" + termsAggregation.getName(),
+ termsQuery(FIELD_RULE_TYPE, VULNERABILITY.name(), SECURITY_HOTSPOT.name()))
+ .subAggregation(termsAggregation);
+ }
}
- private static void addDefaultSecurityFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations,
+ private void addDefaultSecurityFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations,
StickyFacetBuilder stickyFacetBuilder) {
if (options.getFacets().contains(FACET_CWE)) {
Collection<String> categories = query.getCwe();
@@ -771,4 +791,8 @@ public class RuleIndex {
private static boolean isEmpty(@Nullable Collection<?> list) {
return list == null || list.isEmpty();
}
+
+ private boolean isMQRMode() {
+ return config.getBoolean(MULTI_QUALITY_MODE_ENABLED).orElse(MULTI_QUALITY_MODE_DEFAULT_VALUE);
+ }
}