@@ -135,6 +135,9 @@ public class RuleIndex { | |||
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"; | |||
public static final String FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY = "cleanCodeAttributeCategories"; | |||
public static final String FACET_IMPACT_SOFTWARE_QUALITY = "impactSoftwareQualities"; | |||
public static final String FACET_IMPACT_SEVERITY = "impactSeverities"; | |||
private static final int MAX_FACET_SIZE = 100; | |||
@@ -107,30 +107,45 @@ public class CreateActionIT { | |||
.setParam("params", "regex=a.*") | |||
.execute().getInput(); | |||
assertJson(result).isSimilarTo("{\n" + | |||
" \"rule\": {\n" + | |||
" \"key\": \"java:MY_CUSTOM\",\n" + | |||
" \"repo\": \"java\",\n" + | |||
" \"name\": \"My custom rule\",\n" + | |||
" \"htmlDesc\": \"Description\",\n" + | |||
" \"severity\": \"MAJOR\",\n" + | |||
" \"status\": \"BETA\",\n" + | |||
" \"type\": \"BUG\",\n" + | |||
" \"internalKey\": \"configKey_S001\",\n" + | |||
" \"isTemplate\": false,\n" + | |||
" \"templateKey\": \"java:S001\",\n" + | |||
" \"sysTags\": [\"systag1\", \"systag2\"],\n" + | |||
" \"lang\": \"js\",\n" + | |||
" \"params\": [\n" + | |||
" {\n" + | |||
" \"key\": \"regex\",\n" + | |||
" \"htmlDesc\": \"Reg ex\",\n" + | |||
" \"defaultValue\": \"a.*\",\n" + | |||
" \"type\": \"STRING\"\n" + | |||
" }\n" + | |||
" ]\n" + | |||
" }\n" + | |||
"}\n"); | |||
String expetedResult = """ | |||
{ | |||
"rule": { | |||
"key": "java:MY_CUSTOM", | |||
"repo": "java", | |||
"name": "My custom rule", | |||
"htmlDesc": "Description", | |||
"severity": "MAJOR", | |||
"status": "BETA", | |||
"type": "BUG", | |||
"internalKey": "configKey_S001", | |||
"isTemplate": false, | |||
"templateKey": "java:S001", | |||
"sysTags": [ | |||
"systag1", | |||
"systag2" | |||
], | |||
"lang": "js", | |||
"params": [ | |||
{ | |||
"key": "regex", | |||
"htmlDesc": "Reg ex", | |||
"defaultValue": "a.*", | |||
"type": "STRING" | |||
} | |||
], | |||
"cleanCodeAttribute": "CONVENTIONAL", | |||
"cleanCodeAttributeCategory": "CONSISTENT", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "RELIABILITY", | |||
"severity": "MEDIUM" | |||
} | |||
] | |||
} | |||
} | |||
"""; | |||
assertJson(result).isSimilarTo(expetedResult); | |||
} | |||
@Test |
@@ -98,6 +98,7 @@ import static org.sonar.db.rule.RuleTesting.newRule; | |||
import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection; | |||
import static org.sonar.db.rule.RuleTesting.setSystemTags; | |||
import static org.sonar.db.rule.RuleTesting.setTags; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CLEAN_CODE_ATTRIBUTE; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE; | |||
@@ -320,8 +321,7 @@ public class SearchActionIT { | |||
r -> r.setName("Name"), | |||
r -> r.setRepositoryKey("repo_key"), | |||
r -> r.setSeverity("MINOR"), | |||
r -> r.setLanguage("java") | |||
); | |||
r -> r.setLanguage("java")); | |||
indexRules(); | |||
Rules.SearchResponse response = ws.newRequest().executeProtobuf(Rules.SearchResponse.class); | |||
@@ -496,6 +496,27 @@ public class SearchActionIT { | |||
.containsExactly(rule.getTags().toArray(new String[0])); | |||
} | |||
@Test | |||
public void returnRuleCleanCodeFields_whenEndpointIsCalled() { | |||
RuleDto rule = db.rules() | |||
.insert(); | |||
indexRules(); | |||
SearchResponse result = ws.newRequest() | |||
.setParam(WebService.Param.FIELDS, FIELD_CLEAN_CODE_ATTRIBUTE) | |||
.executeProtobuf(SearchResponse.class); | |||
// mandatory fields | |||
assertThat(result.getRulesList()) | |||
.extracting(r -> r.getImpacts().getImpactsList().stream().findFirst().orElseThrow(() -> new IllegalStateException("Impact is a mandatory field in the response."))) | |||
.extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity) | |||
.containsExactly(tuple(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH)); | |||
// selected fields | |||
assertThat(result.getRulesList()).extracting(Rule::getCleanCodeAttribute).containsExactly(Common.CleanCodeAttribute.CLEAR); | |||
assertThat(result.getRulesList()).extracting(Rule::getCleanCodeAttributeCategory).containsExactly(Common.CleanCodeAttributeCategory.INTENTIONAL); | |||
} | |||
@Test | |||
public void should_return_specified_fields() { | |||
when(macroInterpreter.interpret(anyString())).thenAnswer(invocation -> invocation.getArgument(0)); | |||
@@ -523,8 +544,7 @@ public class SearchActionIT { | |||
checkField(rule, "gapDescription", Rule::getGapDescription, rule.getGapDescription()); | |||
checkDescriptionSections(rule, rule.getRuleDescriptionSectionDtos().stream() | |||
.map(SearchActionIT::toProtobufDto) | |||
.collect(Collectors.toSet()) | |||
); | |||
.collect(Collectors.toSet())); | |||
} | |||
private RuleDescriptionSectionDto createRuleDescriptionSectionWithContext(String key, String content, @Nullable String contextKey) { | |||
@@ -995,10 +1015,10 @@ public class SearchActionIT { | |||
indexRules(); | |||
ws.newRequest() | |||
.setParam(WebService.Param.PAGE, "2") | |||
.setParam(WebService.Param.PAGE_SIZE, "9") | |||
.execute() | |||
.assertJson(this.getClass(), "paging.json"); | |||
.setParam(WebService.Param.PAGE, "2") | |||
.setParam(WebService.Param.PAGE_SIZE, "9") | |||
.execute() | |||
.assertJson(this.getClass(), "paging.json"); | |||
} | |||
@Test | |||
@@ -1050,7 +1070,6 @@ public class SearchActionIT { | |||
assertThat(actualSections).hasSameElementsAs(expected); | |||
} | |||
private void verifyNoResults(Consumer<TestRequest> requestPopulator) { | |||
verify(requestPopulator); | |||
} |
@@ -131,6 +131,10 @@ public class ShowActionIT { | |||
assertThat(resultRule.getParams().getParamsList()) | |||
.extracting(Rule.Param::getKey, Rule.Param::getHtmlDesc, Rule.Param::getDefaultValue) | |||
.containsExactlyInAnyOrder(tuple(ruleParam.getName(), ruleParam.getDescription(), ruleParam.getDefaultValue())); | |||
assertThat(resultRule.getImpacts().getImpactsList()) | |||
.extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity) | |||
.containsExactly(tuple(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH)); | |||
assertThat(resultRule.getEducationPrinciples().getEducationPrinciplesList()).containsExactlyElementsOf(rule.getEducationPrinciples()); | |||
} | |||
@@ -146,6 +150,19 @@ public class ShowActionIT { | |||
.containsExactly(rule.getTags().toArray(new String[0])); | |||
} | |||
//<test case name>_when<conditionInCamelCase>_should<assertionInCamelCase> | |||
@Test | |||
public void returnRuleCleanCodeFields_whenEndpointIsCalled() { | |||
RuleDto rule = db.rules().insert(setTags("tag1", "tag2"), r -> r.setNoteData(null).setNoteUserUuid(null)); | |||
ShowResponse result = ws.newRequest() | |||
.setParam(PARAM_KEY, rule.getKey().toString()) | |||
.executeProtobuf(ShowResponse.class); | |||
assertThat(result.getRule().getCleanCodeAttribute()).isEqualTo(Common.CleanCodeAttribute.CLEAR); | |||
assertThat(result.getRule().getCleanCodeAttributeCategory()).isEqualTo(Common.CleanCodeAttributeCategory.INTENTIONAL); | |||
} | |||
@Test | |||
public void show_rule_with_note_login() { | |||
UserDto user = db.users().insertUser(); | |||
@@ -334,7 +351,7 @@ public class ShowActionIT { | |||
@Test | |||
public void show_adhoc_rule() { | |||
//Ad-hoc description has no description sections defined | |||
// Ad-hoc description has no description sections defined | |||
RuleDto externalRule = db.rules().insert(newRuleWithoutDescriptionSection() | |||
.setIsExternal(true) | |||
.setIsAdHoc(true) | |||
@@ -391,8 +408,7 @@ public class ShowActionIT { | |||
tuple(ASSESS_THE_PROBLEM_SECTION_KEY, "<div>This is not a problem</div>", "", ""), | |||
tuple(HOW_TO_FIX_SECTION_KEY, "<div>I don't want to fix</div>", "", ""), | |||
tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Spring</div>", section4context1.getContext().getKey(), section4context1.getContext().getDisplayName()), | |||
tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Servlet</div>", section4context2.getContext().getKey(), section4context2.getContext().getDisplayName()) | |||
); | |||
tuple(RESOURCES_SECTION_KEY, "<div>I want to fix with Servlet</div>", section4context2.getContext().getKey(), section4context2.getContext().getDisplayName())); | |||
} | |||
@Test |
@@ -44,6 +44,7 @@ import org.sonar.server.text.MacroInterpreter; | |||
import org.sonar.server.ws.TestResponse; | |||
import org.sonar.server.ws.WsAction; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.Common; | |||
import org.sonarqube.ws.Rules; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -301,6 +302,29 @@ public class UpdateActionIT { | |||
.isInstanceOf(UnauthorizedException.class); | |||
} | |||
@Test | |||
public void returnRuleCleanCodeFields_whenEndpointIsCalled() { | |||
UserDto userAuthenticated = db.users().insertUser(); | |||
userSession.logIn(userAuthenticated).addPermission(ADMINISTER_QUALITY_PROFILES); | |||
RuleDto rule = db.rules() | |||
.insert(); | |||
Rules.UpdateResponse updateResponse = ws.newRequest().setMethod("POST") | |||
.setParam("key", rule.getKey().toString()) | |||
.executeProtobuf(Rules.UpdateResponse.class); | |||
// mandatory fields | |||
assertThat(updateResponse.getRule()) | |||
.extracting(r -> r.getImpacts().getImpactsList().stream().findFirst().orElseThrow(() -> new IllegalStateException("Impact is a mandatory field in the response."))) | |||
.extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity) | |||
.containsExactly(Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH); | |||
// selected fields | |||
assertThat(updateResponse.getRule()).extracting(Rules.Rule::getCleanCodeAttribute).isEqualTo(Common.CleanCodeAttribute.CLEAR); | |||
assertThat(updateResponse.getRule()).extracting(Rules.Rule::getCleanCodeAttributeCategory).isEqualTo(Common.CleanCodeAttributeCategory.INTENTIONAL); | |||
} | |||
private void logInAsQProfileAdministrator() { | |||
userSession | |||
.logIn() |
@@ -86,8 +86,10 @@ public class CreateAction implements RulesWsAction { | |||
.setResponseExample(Resources.getResource(getClass(), "create-example.json")) | |||
.setSince("4.4") | |||
.setChangelog( | |||
new Change("5.5", "Creating manual rule is not more possible"), | |||
new Change("10.0","Drop deprecated keys: 'custom_key', 'template_key', 'markdown_description', 'prevent_reactivation'"), | |||
new Change("5.5", "Creating manual rule is not more possible")) | |||
new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response") | |||
) | |||
.setHandler(this); | |||
action |
@@ -34,6 +34,7 @@ import org.sonar.api.resources.Languages; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.db.issue.ImpactDto; | |||
import org.sonar.db.rule.DeprecatedRuleKeyDto; | |||
import org.sonar.db.rule.RuleDescriptionSectionContextDto; | |||
import org.sonar.db.rule.RuleDescriptionSectionDto; | |||
@@ -51,6 +52,7 @@ import org.sonarqube.ws.Rules; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.db.rule.RuleDto.Format.MARKDOWN; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CLEAN_CODE_ATTRIBUTE; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CREATED_AT; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_DEBT_REM_FUNCTION; | |||
import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_DEFAULT_DEBT_REM_FUNCTION; | |||
@@ -117,6 +119,7 @@ public class RuleMapper { | |||
// Mandatory fields | |||
ruleResponse.setKey(ruleDto.getKey().toString()); | |||
ruleResponse.setType(Common.RuleType.forNumber(ruleDto.getType())); | |||
setImpacts(ruleResponse, ruleDto); | |||
// Optional fields | |||
setName(ruleResponse, ruleDto, fieldsToReturn); | |||
@@ -148,9 +151,23 @@ public class RuleMapper { | |||
setAdHocType(ruleResponse, ruleDto); | |||
} | |||
setEducationPrinciples(ruleResponse, ruleDto, fieldsToReturn); | |||
setCleanCodeAttributes(ruleResponse, ruleDto, fieldsToReturn); | |||
return ruleResponse; | |||
} | |||
private static void setImpacts(Rules.Rule.Builder ruleResponse, RuleDto ruleDto) { | |||
Rules.Impacts.Builder impactsBuilder = Rules.Impacts.newBuilder(); | |||
ruleDto.getDefaultImpacts().forEach(impactDto -> impactsBuilder.addImpacts(toImpact(impactDto))); | |||
ruleResponse.setImpacts(impactsBuilder.build()); | |||
} | |||
private static Common.Impact toImpact(ImpactDto impactDto) { | |||
Common.ImpactSeverity severity = Common.ImpactSeverity.valueOf(impactDto.getSeverity().name()); | |||
Common.SoftwareQuality softwareQuality = Common.SoftwareQuality.valueOf(impactDto.getSoftwareQuality().name()); | |||
return Common.Impact.newBuilder().setSeverity(severity).setSoftwareQuality(softwareQuality).build(); | |||
} | |||
private static void setAdHocName(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn) { | |||
String adHocName = ruleDto.getAdHocName(); | |||
if (adHocName != null && shouldReturnField(fieldsToReturn, FIELD_NAME)) { | |||
@@ -204,6 +221,13 @@ public class RuleMapper { | |||
} | |||
} | |||
private static void setCleanCodeAttributes(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn) { | |||
if(shouldReturnField(fieldsToReturn, FIELD_CLEAN_CODE_ATTRIBUTE)){ | |||
ruleResponse.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(ruleDto.getCleanCodeAttribute().name())); | |||
ruleResponse.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(ruleDto.getCleanCodeAttribute().getAttributeCategory().name())); | |||
} | |||
} | |||
private static void setDeprecatedKeys(Rules.Rule.Builder ruleResponse, RuleDto ruleDto, Set<String> fieldsToReturn, | |||
Map<String, List<DeprecatedRuleKeyDto>> deprecatedRuleKeysByRuleUuid) { | |||
if (shouldReturnField(fieldsToReturn, FIELD_DEPRECATED_KEYS)) { |
@@ -70,6 +70,7 @@ public class RulesWsParameters { | |||
public static final String FIELD_NOTE_LOGIN = "noteLogin"; | |||
public static final String FIELD_MARKDOWN_NOTE = "mdNote"; | |||
public static final String FIELD_HTML_NOTE = "htmlNote"; | |||
public static final String FIELD_CLEAN_CODE_ATTRIBUTE = "cleanCodeAttribute"; | |||
/** | |||
* Value for 'f' parameter which is used to return all the "defaultDebtRemFn" fields. | |||
@@ -112,7 +113,7 @@ public class RulesWsParameters { | |||
FIELD_MARKDOWN_DESCRIPTION, FIELD_DESCRIPTION_SECTIONS, FIELD_NOTE_LOGIN, FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE, | |||
FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_DEBT_REM_FUNCTION, | |||
FIELD_DEFAULT_REM_FUNCTION, FIELD_GAP_DESCRIPTION, FIELD_REM_FUNCTION_OVERLOADED, FIELD_REM_FUNCTION, | |||
FIELD_PARAMS, FIELD_ACTIVES, FIELD_SCOPE, FIELD_DEPRECATED_KEYS, FIELD_EDUCATION_PRINCIPLES); | |||
FIELD_PARAMS, FIELD_ACTIVES, FIELD_SCOPE, FIELD_DEPRECATED_KEYS, FIELD_EDUCATION_PRINCIPLES, FIELD_CLEAN_CODE_ATTRIBUTE); | |||
private RulesWsParameters() { | |||
// prevent instantiation |
@@ -66,7 +66,10 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||
import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE; | |||
import static org.sonar.server.rule.index.RuleIndex.ALL_STATUSES_EXCEPT_REMOVED; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_ACTIVE_SEVERITIES; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_CWE; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_IMPACT_SEVERITY; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_IMPACT_SOFTWARE_QUALITY; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_LANGUAGES; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_OLD_DEFAULT; | |||
import static org.sonar.server.rule.index.RuleIndex.FACET_OWASP_TOP_10; | |||
@@ -111,7 +114,11 @@ public class SearchAction implements RulesWsAction { | |||
FACET_OWASP_TOP_10, | |||
FACET_OWASP_TOP_10_2021, | |||
FACET_SANS_TOP_25, | |||
FACET_SONARSOURCE_SECURITY}; | |||
FACET_SONARSOURCE_SECURITY, | |||
FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY, | |||
FACET_IMPACT_SEVERITY, | |||
FACET_IMPACT_SOFTWARE_QUALITY | |||
}; | |||
private final RuleQueryFactory ruleQueryFactory; | |||
private final DbClient dbClient; | |||
@@ -166,7 +173,11 @@ public class SearchAction implements RulesWsAction { | |||
new Change("10.0", "The value 'defaultDebtRemFn' for the 'f' parameter has been deprecated, use 'defaultRemFn' instead"), | |||
new Change("10.0", "The value 'sansTop25' for the parameter 'facets' has been deprecated"), | |||
new Change("10.0", "Parameter 'sansTop25' is deprecated"), | |||
new Change("10.2", format("Parameters '%s', '%s', and '%s' are now deprecated.", PARAM_SEVERITIES, PARAM_TYPES, PARAM_ACTIVE_SEVERITIES))); | |||
new Change("10.2", format("Parameters '%s', '%s', and '%s' are now deprecated.", PARAM_SEVERITIES, PARAM_TYPES, PARAM_ACTIVE_SEVERITIES)), | |||
new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"), | |||
new Change("10.2", "The field 'cleanCodeAttribute' has been added to the 'f' parameter"), | |||
new Change("10.2", format("add '%s', '%s' and '%s' to the 'facets' parameter.",FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY, FACET_IMPACT_SOFTWARE_QUALITY, FACET_IMPACT_SEVERITY)) | |||
); | |||
action.createParam(FACETS) | |||
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.") |
@@ -87,8 +87,8 @@ public class ShowAction implements RulesWsAction { | |||
new Change("10.0", "The deprecated field 'defaultDebtRemFnOffset' has been removed, use 'defaultRemFnBaseEffort' instead."), | |||
new Change("10.0", "The deprecated field 'debtOverloaded' has been removed, use 'remFnOverloaded' instead."), | |||
new Change("10.0", "The field 'defaultDebtRemFnType' has been deprecated, use 'defaultRemFnType' instead"), | |||
new Change("10.0", "The field 'debtRemFnType' has been deprecated, use 'remFnType' instead") | |||
); | |||
new Change("10.0", "The field 'debtRemFnType' has been deprecated, use 'remFnType' instead"), | |||
new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response")); | |||
action | |||
.createParam(PARAM_KEY) |
@@ -30,6 +30,7 @@ import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.api.server.ws.Change; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
@@ -90,6 +91,8 @@ public class UpdateAction implements RulesWsAction { | |||
.setDescription("Update an existing rule.<br>" + | |||
"Requires the 'Administer Quality Profiles' permission") | |||
.setSince("4.4") | |||
.setChangelog( | |||
new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response")) | |||
.setHandler(this); | |||
action.createParam(PARAM_KEY) |
@@ -1,28 +1,36 @@ | |||
{ | |||
"rule":{ | |||
"key":"squid:forbidSonar", | |||
"repo":"squid", | |||
"name":"forbidSonar", | |||
"createdAt":"2018-06-06T16:04:28+0200", | |||
"htmlDesc":"Forbid classes with name starting with Sonar", | |||
"mdDesc":"Forbid classes with name starting with Sonar", | |||
"severity":"MAJOR", | |||
"status":"READY", | |||
"isTemplate":false, | |||
"templateKey":"squid:S3688", | |||
"sysTags":[], | |||
"lang":"java", | |||
"langName":"Java", | |||
"params":[ | |||
{ | |||
"key":"className", | |||
"htmlDesc":"Fully qualified name of the forbidden class. Use a regex to forbid a package.", | |||
"defaultValue":"**/Sonar*", | |||
"type":"STRING" | |||
} | |||
"rule": { | |||
"key": "squid:forbidSonar", | |||
"repo": "squid", | |||
"name": "forbidSonar", | |||
"createdAt": "2018-06-06T16:04:28+0200", | |||
"htmlDesc": "Forbid classes with name starting with Sonar", | |||
"mdDesc": "Forbid classes with name starting with Sonar", | |||
"severity": "MAJOR", | |||
"status": "READY", | |||
"isTemplate": false, | |||
"templateKey": "squid:S3688", | |||
"sysTags": [], | |||
"lang": "java", | |||
"langName": "Java", | |||
"params": [ | |||
{ | |||
"key": "className", | |||
"htmlDesc": "Fully qualified name of the forbidden class. Use a regex to forbid a package.", | |||
"defaultValue": "**/Sonar*", | |||
"type": "STRING" | |||
} | |||
], | |||
"scope":"MAIN", | |||
"isExternal":false, | |||
"type":"CODE_SMELL" | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "CODE_SMELL", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "MAINTAINABILITY", | |||
"severity": "HIGH" | |||
} | |||
] | |||
} | |||
} |
@@ -17,12 +17,22 @@ | |||
"internalKey": "S1067", | |||
"isTemplate": false, | |||
"tags": [], | |||
"sysTags": ["brain-overload"], | |||
"sysTags": [ | |||
"brain-overload" | |||
], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "CODE_SMELL", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "MAINTAINABILITY", | |||
"severity": "HIGH" | |||
} | |||
], | |||
"descriptionSections": [ | |||
{ | |||
"key": "root_cause", | |||
@@ -65,12 +75,22 @@ | |||
"internalKey": "ClassCyclomaticComplexity", | |||
"isTemplate": false, | |||
"tags": [], | |||
"sysTags": ["brain-overload"], | |||
"sysTags": [ | |||
"brain-overload" | |||
], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "BUG", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "RELIABILITY", | |||
"severity": "HIGH" | |||
} | |||
], | |||
"params": [ | |||
{ | |||
"key": "max", | |||
@@ -91,12 +111,22 @@ | |||
"internalKey": "MethodCyclomaticComplexity", | |||
"isTemplate": false, | |||
"tags": [], | |||
"sysTags": ["brain-overload"], | |||
"sysTags": [ | |||
"brain-overload" | |||
], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "VULNERABILITY", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "SECURITY", | |||
"severity": "HIGH" | |||
} | |||
], | |||
"params": [ | |||
{ | |||
"key": "max", | |||
@@ -116,8 +146,8 @@ | |||
"status": "READY", | |||
"internalKey": "XPath", | |||
"isTemplate": true, | |||
"tags": [ ], | |||
"sysTags": [ ], | |||
"tags": [], | |||
"sysTags": [], | |||
"mdNote": "<p>\nThe tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.\n</p>\n\n<p>\nConsider the following rule:\n</p>\n\n<pre>\nb.rule(COMPILATION_UNIT).is(\n b.firstOf( /* Non-Compliant */\n \"FOO\",\n \"BAR\"));\n</pre>\n\n<p>\nIf, from a check, one wants to forbid the usage of the \"BAR\" alternative,\nthe easiest option will be to verify that the value of the first token is \"BAR\",\ni.e. <code>\"BAR\".equals(compilationUnitNode.getTokenValue())</code>.\n</p>\n\n<p>\nThis is not maintainable, for at least two reasons:\n</p>\n\n<ul>\n <li>The grammar might evolve to also accept \"bar\" in lowercase, which will break <code>\"BAR\".equals(...)</code></li>\n <li>The grammar might evolve to optionally accept \"hello\" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li>\n</ul>\n\n<p>\nInstead, it is much better to rewrite the grammar as:\n</p>\n\n<pre>\nb.rule(COMPILATION_UNIT).is(\n firstOf( /* Compliant */\n FOO,\n BAR));\nb.rule(FOO).is(\"FOO\");\nb.rule(BAR).is(\"BAR\");\n</pre>\n\n<p>\nThe same check which forbids \"BAR\" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.\nThis allows both of the previous grammar evolutions to be made without impacting the check at all.\n</p>", | |||
"htmlNote": "<p><br/>The tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.<br/></p><br/><br/><p><br/>Consider the following rule:<br/></p><br/><br/><pre><br/>b.rule(COMPILATION_UNIT).is(<br/> b.firstOf( /* Non-Compliant */<br/> "FOO",<br/> "BAR"));<br/></pre><br/><br/><p><br/>If, from a check, one wants to forbid the usage of the "BAR" alternative,<br/>the easiest option will be to verify that the value of the first token is "BAR",<br/>i.e. <code>"BAR".equals(compilationUnitNode.getTokenValue())</code>.<br/></p><br/><br/><p><br/>This is not maintainable, for at least two reasons:<br/></p><br/><br/><ul><br/> <li>The grammar might evolve to also accept "bar" in lowercase, which will break <code>"BAR".equals(...)</code></li><br/> <li>The grammar might evolve to optionally accept "hello" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li><br/></ul><br/><br/><p><br/>Instead, it is much better to rewrite the grammar as:<br/></p><br/><br/><pre><br/>b.rule(COMPILATION_UNIT).is(<br/> firstOf( /* Compliant */<br/> FOO,<br/> BAR));<br/>b.rule(FOO).is("FOO");<br/>b.rule(BAR).is("BAR");<br/></pre><br/><br/><p><br/>The same check which forbids "BAR" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.<br/>This allows both of the previous grammar evolutions to be made without impacting the check at all.<br/></p>", | |||
"noteLogin": "eric.hartmann", | |||
@@ -151,13 +181,21 @@ | |||
"internalKey": "XPath", | |||
"isTemplate": false, | |||
"templateKey": "squid:XPath", | |||
"tags": [ ], | |||
"sysTags": [ ], | |||
"tags": [], | |||
"sysTags": [], | |||
"lang": "java", | |||
"langName": "Java", | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "CODE_SMELL", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "MAINTAINABILITY", | |||
"severity": "HIGH" | |||
} | |||
], | |||
"params": [ | |||
{ | |||
"key": "xpathQuery", | |||
@@ -290,6 +328,5 @@ | |||
} | |||
] | |||
} | |||
] | |||
} |
@@ -1,4 +1,5 @@ | |||
{"rule": { | |||
{ | |||
"rule": { | |||
"key": "squid:ClassCyclomaticComplexity", | |||
"repo": "squid", | |||
"name": "Avoid too complex class", | |||
@@ -8,7 +9,9 @@ | |||
"internalKey": "ClassCyclomaticComplexity", | |||
"template": false, | |||
"tags": [], | |||
"sysTags": ["brain-overload"], | |||
"sysTags": [ | |||
"brain-overload" | |||
], | |||
"remFnType": "LINEAR_OFFSET", | |||
"remFnGapMultiplier": "5d", | |||
"remFnBaseEffort": "10h", | |||
@@ -22,56 +25,66 @@ | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "CODE_SMELL", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "MAINTAINABILITY", | |||
"severity": "HIGH" | |||
} | |||
], | |||
"descriptionSections": [ | |||
{ | |||
"key": "root_cause", | |||
"content": "<h3 class=\"page-title coding-rules-detail-header\"><big>Unnecessary imports should be removed</big></h3>" | |||
}, | |||
{ | |||
"key": "how_to_fix", | |||
"content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate Spring Security's CSRF protection. </li></ul>", | |||
"context": { | |||
"displayName": "Spring", | |||
"key": "spring" | |||
} | |||
}, | |||
{ | |||
"key": "how_to_fix", | |||
"content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate hibernate protection. </li></ul>", | |||
"context": { | |||
"displayName": "Hibernate", | |||
"key": "hibernate" | |||
} | |||
{ | |||
"key": "root_cause", | |||
"content": "<h3 class=\"page-title coding-rules-detail-header\"><big>Unnecessary imports should be removed</big></h3>" | |||
}, | |||
{ | |||
"key": "how_to_fix", | |||
"content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate Spring Security's CSRF protection. </li></ul>", | |||
"context": { | |||
"displayName": "Spring", | |||
"key": "spring" | |||
} | |||
}, | |||
{ | |||
"key": "how_to_fix", | |||
"content": "<h2>Recommended Secure Coding Practices</h2><ul><li> activate hibernate protection. </li></ul>", | |||
"context": { | |||
"displayName": "Hibernate", | |||
"key": "hibernate" | |||
} | |||
} | |||
], | |||
"params": [ | |||
{ | |||
"key": "max", | |||
"desc": "Maximum complexity allowed.", | |||
"defaultValue": "200" | |||
} | |||
{ | |||
"key": "max", | |||
"desc": "Maximum complexity allowed.", | |||
"defaultValue": "200" | |||
} | |||
] | |||
}, "actives": [ | |||
}, | |||
"actives": [ | |||
{ | |||
"qProfile": "Sonar way with Findbugs:java", | |||
"inherit": "NONE", | |||
"severity": "MAJOR", | |||
"params": [ | |||
{ | |||
"key": "max", | |||
"value": "200" | |||
} | |||
] | |||
"qProfile": "Sonar way with Findbugs:java", | |||
"inherit": "NONE", | |||
"severity": "MAJOR", | |||
"params": [ | |||
{ | |||
"key": "max", | |||
"value": "200" | |||
} | |||
] | |||
}, | |||
{ | |||
"qProfile": "Sonar way:java", | |||
"inherit": "NONE", | |||
"severity": "MAJOR", | |||
"params": [ | |||
{ | |||
"key": "max", | |||
"value": "200" | |||
} | |||
] | |||
"qProfile": "Sonar way:java", | |||
"inherit": "NONE", | |||
"severity": "MAJOR", | |||
"params": [ | |||
{ | |||
"key": "max", | |||
"value": "200" | |||
} | |||
] | |||
} | |||
]} | |||
] | |||
} |
@@ -1,29 +1,38 @@ | |||
{ | |||
"rule":{ | |||
"key":"squid:forbidSonar", | |||
"repo":"squid", | |||
"name":"forbidSonar", | |||
"createdAt":"2018-06-06T16:09:09+0200", | |||
"htmlDesc":"Forbid classes with name starting with Sonar", | |||
"mdDesc":"Forbid classes with name starting with Sonar", | |||
"severity":"MAJOR", | |||
"status":"READY", | |||
"isTemplate":false, | |||
"templateKey":"squid:S3688", | |||
"tags":[], | |||
"sysTags":[], | |||
"lang":"java", | |||
"langName":"Java", | |||
"params":[ | |||
{ | |||
"key":"className", | |||
"htmlDesc":"Fully qualified name of the forbidden class. Use a regex to forbid a package.", | |||
"defaultValue":"**/Sonar*","type":"STRING" | |||
} | |||
"rule": { | |||
"key": "squid:forbidSonar", | |||
"repo": "squid", | |||
"name": "forbidSonar", | |||
"createdAt": "2018-06-06T16:09:09+0200", | |||
"htmlDesc": "Forbid classes with name starting with Sonar", | |||
"mdDesc": "Forbid classes with name starting with Sonar", | |||
"severity": "MAJOR", | |||
"status": "READY", | |||
"isTemplate": false, | |||
"templateKey": "squid:S3688", | |||
"tags": [], | |||
"sysTags": [], | |||
"lang": "java", | |||
"langName": "Java", | |||
"params": [ | |||
{ | |||
"key": "className", | |||
"htmlDesc": "Fully qualified name of the forbidden class. Use a regex to forbid a package.", | |||
"defaultValue": "**/Sonar*", | |||
"type": "STRING" | |||
} | |||
], | |||
"remFnOverloaded":false, | |||
"scope":"MAIN", | |||
"isExternal":false, | |||
"type":"CODE_SMELL" | |||
"remFnOverloaded": false, | |||
"scope": "MAIN", | |||
"isExternal": false, | |||
"type": "CODE_SMELL", | |||
"cleanCodeAttributeCategory": "INTENTIONAL", | |||
"cleanCodeAttribute": "CLEAR", | |||
"impacts": [ | |||
{ | |||
"softwareQuality": "MAINTAINABILITY", | |||
"severity": "HIGH" | |||
} | |||
] | |||
} | |||
} |
@@ -41,9 +41,9 @@ message ListResponse { | |||
// WS api/rules/search | |||
message SearchResponse { | |||
optional int64 total = 1 [deprecated=true]; | |||
optional int32 p = 2 [deprecated=true]; | |||
optional int64 ps = 3 [deprecated=true]; | |||
optional int64 total = 1 [deprecated = true]; | |||
optional int32 p = 2 [deprecated = true]; | |||
optional int64 ps = 3 [deprecated = true]; | |||
repeated Rule rules = 4; | |||
optional Actives actives = 5; | |||
@@ -73,12 +73,13 @@ message Rule { | |||
optional string repo = 2; | |||
optional string name = 3; | |||
optional string createdAt = 4; | |||
optional string htmlDesc = 5 [deprecated=true]; | |||
optional string htmlDesc = 5 [deprecated = true]; | |||
optional string htmlNote = 6; | |||
optional string mdDesc = 7; | |||
optional string mdNote = 8; | |||
optional string noteLogin = 9; | |||
optional string severity = 10; | |||
// Deprecated since 10.2, replace by impacts | |||
optional string severity = 10 [deprecated = true]; | |||
optional sonarqube.ws.commons.RuleStatus status = 11; | |||
optional string internalKey = 12; | |||
optional bool isTemplate = 13; | |||
@@ -97,16 +98,17 @@ message Rule { | |||
optional string unusedDebtSubCharName = 28; | |||
// Deprecated since 10.0, replaced by defaultRemFnType | |||
optional string defaultDebtRemFnType = 29 [deprecated=true]; | |||
optional string defaultDebtRemFnType = 29 [deprecated = true]; | |||
reserved 30; | |||
reserved 31; | |||
reserved 32; | |||
reserved 33; | |||
// Deprecated since 10.0, replaced by remFnType | |||
optional string debtRemFnType = 34 [deprecated=true]; | |||
optional string debtRemFnType = 34 [deprecated = true]; | |||
reserved 35; | |||
reserved 36; | |||
optional sonarqube.ws.commons.RuleType type = 37; | |||
// Deprecated since 10.2, replace by impacts | |||
optional sonarqube.ws.commons.RuleType type = 37 [deprecated = true]; | |||
optional string defaultRemFnType = 38; | |||
optional string defaultRemFnGapMultiplier = 39; | |||
optional string defaultRemFnBaseEffort = 40; | |||
@@ -121,6 +123,9 @@ message Rule { | |||
optional DescriptionSections descriptionSections = 49; | |||
optional EducationPrinciples educationPrinciples = 50; | |||
optional string updatedAt = 51; | |||
optional sonarqube.ws.commons.CleanCodeAttribute cleanCodeAttribute = 52; | |||
optional sonarqube.ws.commons.CleanCodeAttributeCategory cleanCodeAttributeCategory = 53; | |||
optional Impacts impacts = 54; | |||
message DescriptionSections { | |||
repeated DescriptionSection descriptionSections = 1; | |||
@@ -149,6 +154,10 @@ message Rule { | |||
} | |||
} | |||
message Impacts{ | |||
repeated sonarqube.ws.commons.Impact impacts = 1; | |||
} | |||
message DeprecatedKeys { | |||
repeated string deprecatedKey = 1; | |||
} |