dependency 'org.sonarsource.kotlin:sonar-kotlin-plugin:2.13.0.2116'
dependency 'org.sonarsource.slang:sonar-ruby-plugin:1.12.0.4259'
dependency 'org.sonarsource.slang:sonar-scala-plugin:1.12.0.4259'
- dependency "org.sonarsource.api.plugin:sonar-plugin-api:$pluginApiVersion"
- dependency "org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures:$pluginApiVersion"
+ dependency "org.sonarsource.api.plugin:sonar-plugin-api:9.16.0.500"
+ dependency "org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures:9.16.0.500"
dependency 'org.sonarsource.xml:sonar-xml-plugin:2.7.0.3820'
dependency 'org.sonarsource.iac:sonar-iac-plugin:1.15.0.3752'
dependency 'org.sonarsource.text:sonar-text-plugin:2.0.2.1090'
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Collections.emptySet;
import static java.util.Optional.ofNullable;
import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY;
+import static org.sonar.db.rule.RuleTypeToRuleCharacteristicConverter.convertToRuleCharacteristic;
public class RuleDto {
private String systemTagsField = null;
private String securityStandardsField = null;
private int type = 0;
+ private RuleCharacteristic characteristic = null;
private Scope scope = null;
private RuleKey key = null;
return deserializeStringSet(educationPrinciplesField);
}
- public RuleDto setEducationPrinciples(Set<String> educationPrinciples){
+ public RuleDto setEducationPrinciples(Set<String> educationPrinciples) {
this.educationPrinciplesField = serializeStringSet(educationPrinciples);
return this;
}
return this;
}
+ @CheckForNull
+ public RuleCharacteristic getCharacteristic() {
+ return this.characteristic;
+ }
+
+ @CheckForNull
+ public RuleCharacteristic getEffectiveCharacteristic() {
+ return characteristic != null ? characteristic : convertTypeToCharacteristic(type);
+ }
+
+ private static RuleCharacteristic convertTypeToCharacteristic(int type) {
+ RuleType ruleType = RuleType.valueOf(type);
+ return convertToRuleCharacteristic(ruleType);
+ }
+
+ public RuleDto setCharacteristic(RuleCharacteristic characteristic) {
+ this.characteristic = characteristic;
+ return this;
+ }
+
public long getCreatedAt() {
return createdAt;
}
return this;
}
-
@CheckForNull
public String getDefRemediationFunction() {
return defRemediationFunction;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.rule;
+
+import org.sonar.api.rules.RuleCharacteristic;
+import org.sonar.api.rules.RuleType;
+
+public class RuleTypeToRuleCharacteristicConverter {
+
+ private RuleTypeToRuleCharacteristicConverter() {
+ }
+
+ public static RuleCharacteristic convertToRuleCharacteristic(RuleType ruleType) {
+ return switch (ruleType) {
+ case BUG -> RuleCharacteristic.ROBUST;
+ case CODE_SMELL -> RuleCharacteristic.CLEAR;
+ case SECURITY_HOTSPOT, VULNERABILITY -> RuleCharacteristic.SECURE;
+ };
+ }
+
+}
r.ad_hoc_description as "adHocDescription",
r.ad_hoc_severity as "adHocSeverity",
r.ad_hoc_type as "adHocType",
- r.education_principles as "educationPrinciplesField"
+ r.education_principles as "educationPrinciplesField",
+ r.characteristic as "characteristic"
</sql>
<sql id="leftOuterJoinRulesDescriptionSections">
"AD_HOC_DESCRIPTION" CHARACTER LARGE OBJECT,
"AD_HOC_SEVERITY" CHARACTER VARYING(10),
"AD_HOC_TYPE" TINYINT,
- "EDUCATION_PRINCIPLES" CHARACTER VARYING(255)
+ "EDUCATION_PRINCIPLES" CHARACTER VARYING(255),
+ "CHARACTERISTIC" CHARACTER VARYING(20)
);
ALTER TABLE "RULES" ADD CONSTRAINT "PK_RULES" PRIMARY KEY("UUID");
CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES"("PLUGIN_RULE_KEY" NULLS FIRST, "PLUGIN_NAME" NULLS FIRST);
package org.sonar.db.rule;
import com.google.common.collect.ImmutableSet;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import org.jetbrains.annotations.NotNull;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.rules.RuleCharacteristic;
+import org.sonar.api.rules.RuleType;
import org.sonar.core.util.Uuids;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.sonar.db.rule.RuleDto.ERROR_MESSAGE_SECTION_ALREADY_EXISTS;
import static org.sonar.db.rule.RuleTesting.newRule;
+@RunWith(DataProviderRunner.class)
public class RuleDtoTest {
-
public static final String SECTION_KEY = "section key";
+
@Test
public void fail_if_key_is_too_long() {
assertThatThrownBy(() -> new RuleDto().setRuleKey(repeat("x", 250)))
.key(section_key)
.build();
}
+
+ @Test
+ public void getEffectiveCharacteristic_when_noCharacteristicInitialized_should_return_dbConstantValueFromConverter() {
+ RuleDto rule = new RuleDto().setType(RuleType.BUG);
+
+ RuleCharacteristic characteristic = rule.getCharacteristic();
+ assertThat(characteristic).isNull();
+
+ RuleCharacteristic effectiveCharacteristic = rule.getEffectiveCharacteristic();
+ RuleCharacteristic expected = RuleTypeToRuleCharacteristicConverter.convertToRuleCharacteristic(RuleType.BUG);
+ assertThat(effectiveCharacteristic).isEqualTo(expected);
+ }
+
+ @Test
+ public void getEffectiveCharacteristic_when_characteristicInitialized_should_return_characteristicDbConstantValue() {
+ RuleDto rule = new RuleDto().setType(RuleType.BUG).setCharacteristic(RuleCharacteristic.COMPLIANT);
+
+ RuleCharacteristic effectiveCharacteristic = rule.getEffectiveCharacteristic();
+ RuleCharacteristic characteristic = rule.getCharacteristic();
+
+ assertThat(effectiveCharacteristic).isEqualTo(characteristic).isEqualTo(RuleCharacteristic.COMPLIANT);
+ }
+
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.rule;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.rules.RuleCharacteristic;
+import org.sonar.api.rules.RuleType;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.rule.RuleTypeToRuleCharacteristicConverter.convertToRuleCharacteristic;
+
+@RunWith(DataProviderRunner.class)
+public class RuleTypeToRuleCharacteristicConverterTest {
+
+ @Test
+ @UseDataProvider("ruleTypeToRulCharacteristicData")
+ public void convertToRuleCharacteristic_when_receivedNonNullRuleType_should_convertToCorrespondingDefaultCharacteristic(RuleType type,
+ RuleCharacteristic expectedCharacteristic) {
+ assertThat(convertToRuleCharacteristic(type)).isEqualTo(expectedCharacteristic);
+ }
+
+ @DataProvider
+ public static Object[][] ruleTypeToRulCharacteristicData() {
+ return new Object[][] {
+ {RuleType.CODE_SMELL, RuleCharacteristic.CLEAR},
+ {RuleType.BUG, RuleCharacteristic.ROBUST},
+ {RuleType.VULNERABILITY, RuleCharacteristic.SECURE},
+ {RuleType.SECURITY_HOTSPOT, RuleCharacteristic.SECURE}
+ };
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v101;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.def.ColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddCharacteristicInRules extends DdlChange {
+
+ private static final String TABLE_NAME = "rules";
+ private static final String COLUMN_NAME = "characteristic";
+
+ public AddCharacteristicInRules(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ try (Connection c = getDatabase().getDataSource().getConnection()) {
+ if (!DatabaseUtils.tableColumnExists(c, TABLE_NAME, COLUMN_NAME)) {
+
+ ColumnDef columnDef = VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName(COLUMN_NAME)
+ .setLimit(20)
+ .setIsNullable(true)
+ .build();
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME).addColumn(columnDef).build());
+ }
+ }
+
+ }
+}
.add(10_1_006, "Update value of 'is_main' in 'project_branches' table", UpdateIsMainColumnInProjectBranches.class)
.add(10_1_007, "Alter column 'is_main' in 'project_branches' table - make it not nullable", AlterIsMainColumnInProjectBranches.class)
.add(10_1_008, "Increase size of 'internal_properties.kee' from 20 to 40 characters", IncreaseKeeColumnSizeInInternalProperties.class)
- ;
+ .add(10_1_009, "Add column 'characteristic' to 'rules' table", AddCharacteristicInRules.class);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v101;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddCharacteristicInRulesTest {
+
+ private static final String TABLE_NAME = "rules";
+ private static final String COLUMN_NAME = "characteristic";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(AddCharacteristicInRulesTest.class, "schema.sql");
+
+ private final AddCharacteristicInRules underTest = new AddCharacteristicInRules(db.database());
+
+ @Test
+ public void characteristic_column_exists_with_null_value() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 20, true);
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 20, true);
+ }
+
+}
--- /dev/null
+CREATE TABLE "RULES"(
+ "UUID" CHARACTER VARYING(40) NOT NULL,
+ "NAME" CHARACTER VARYING(200),
+ "PLUGIN_RULE_KEY" CHARACTER VARYING(200) NOT NULL,
+ "PLUGIN_KEY" CHARACTER VARYING(200),
+ "PLUGIN_CONFIG_KEY" CHARACTER VARYING(200),
+ "PLUGIN_NAME" CHARACTER VARYING(255) NOT NULL,
+ "SCOPE" CHARACTER VARYING(20) NOT NULL,
+ "PRIORITY" INTEGER,
+ "STATUS" CHARACTER VARYING(40),
+ "LANGUAGE" CHARACTER VARYING(20),
+ "DEF_REMEDIATION_FUNCTION" CHARACTER VARYING(20),
+ "DEF_REMEDIATION_GAP_MULT" CHARACTER VARYING(20),
+ "DEF_REMEDIATION_BASE_EFFORT" CHARACTER VARYING(20),
+ "GAP_DESCRIPTION" CHARACTER VARYING(4000),
+ "SYSTEM_TAGS" CHARACTER VARYING(4000),
+ "IS_TEMPLATE" BOOLEAN DEFAULT FALSE NOT NULL,
+ "DESCRIPTION_FORMAT" CHARACTER VARYING(20),
+ "RULE_TYPE" TINYINT,
+ "SECURITY_STANDARDS" CHARACTER VARYING(4000),
+ "IS_AD_HOC" BOOLEAN NOT NULL,
+ "IS_EXTERNAL" BOOLEAN NOT NULL,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "TEMPLATE_UUID" CHARACTER VARYING(40),
+ "NOTE_DATA" CHARACTER LARGE OBJECT,
+ "NOTE_USER_UUID" CHARACTER VARYING(255),
+ "NOTE_CREATED_AT" BIGINT,
+ "NOTE_UPDATED_AT" BIGINT,
+ "REMEDIATION_FUNCTION" CHARACTER VARYING(20),
+ "REMEDIATION_GAP_MULT" CHARACTER VARYING(20),
+ "REMEDIATION_BASE_EFFORT" CHARACTER VARYING(20),
+ "TAGS" CHARACTER VARYING(4000),
+ "AD_HOC_NAME" CHARACTER VARYING(200),
+ "AD_HOC_DESCRIPTION" CHARACTER LARGE OBJECT,
+ "AD_HOC_SEVERITY" CHARACTER VARYING(10),
+ "AD_HOC_TYPE" TINYINT,
+ "EDUCATION_PRINCIPLES" CHARACTER VARYING(255)
+);
+ALTER TABLE "RULES" ADD CONSTRAINT "PK_RULES" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES"("PLUGIN_RULE_KEY" NULLS FIRST, "PLUGIN_NAME" NULLS FIRST);
import org.sonar.api.issue.IssueComment;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.tracking.Trackable;
private String key = null;
private RuleType type = null;
+ private RuleCharacteristic characteristic = null;
private String componentUuid = null;
private String componentKey = null;
return this;
}
+ @CheckForNull
+ @Override
+ public RuleCharacteristic characteristic() {
+ return characteristic;
+ }
+
+ public DefaultIssue setCharacteristic(RuleCharacteristic characteristic) {
+ this.characteristic = characteristic;
+ return this;
+ }
+
/**
* Can be null on Views or Devs
*/
import org.sonar.api.batch.sensor.internal.SensorStorage;
import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.batch.sensor.issue.NewExternalIssue;
-import org.sonar.api.code.CodeCharacteristic;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import static java.lang.String.format;
private Long effort;
private Severity severity;
private RuleType type;
+ private RuleCharacteristic characteristic;
private String engineId;
private String ruleId;
}
@CheckForNull
- @Override
- public CodeCharacteristic characteristic() {
- throw new IllegalStateException("Not implemented yet");
+ public RuleCharacteristic characteristic() {
+ return characteristic;
}
@Override
}
@Override
- public DefaultExternalIssue type(RuleType type) {
- this.type = type;
+ public DefaultExternalIssue characteristic(RuleCharacteristic characteristic) {
+ this.characteristic = characteristic;
return this;
}
@Override
- public NewExternalIssue characteristic(CodeCharacteristic characteristic) {
- throw new IllegalStateException("Not implemented yet");
+ public DefaultExternalIssue type(RuleType type) {
+ this.type = type;
+ return this;
}
}
import org.sonar.api.batch.sensor.internal.SensorStorage;
import org.sonar.api.batch.sensor.rule.AdHocRule;
import org.sonar.api.batch.sensor.rule.NewAdHocRule;
-import org.sonar.api.code.CodeCharacteristic;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import static org.apache.commons.lang.StringUtils.isNotBlank;
public class DefaultAdHocRule extends DefaultStorable implements AdHocRule, NewAdHocRule {
private Severity severity;
private RuleType type;
+
+ private RuleCharacteristic characteristic;
private String name;
private String description;
private String engineId;
@CheckForNull
@Override
- public CodeCharacteristic characteristic() {
- throw new IllegalStateException("Not implemented yet");
+ public RuleCharacteristic characteristic() {
+ return characteristic;
}
@Override
}
@Override
- public NewAdHocRule characteristic(CodeCharacteristic characteristic) {
- throw new IllegalStateException("Not implemented yet");
+ public DefaultAdHocRule characteristic(RuleCharacteristic characteristic) {
+ this.characteristic = characteristic;
+ return this;
}
-
}
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.internal.SensorStorage;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import static org.assertj.core.api.Assertions.assertThat;
.forRule(RuleKey.of("repo", "rule"))
.remediationEffortMinutes(10L)
.type(RuleType.BUG)
- .severity(Severity.BLOCKER);
+ .severity(Severity.BLOCKER)
+ .characteristic(RuleCharacteristic.SECURE);
assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile);
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("external_repo", "rule"));
assertThat(issue.remediationEffort()).isEqualTo(10L);
assertThat(issue.type()).isEqualTo(RuleType.BUG);
assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issue.characteristic()).isEqualTo(RuleCharacteristic.SECURE);
assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!");
issue.save();
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.internal.SensorStorage;
import org.sonar.api.batch.sensor.rule.NewAdHocRule;
+import org.sonar.api.rules.RuleCharacteristic;
import org.sonar.api.rules.RuleType;
import static org.assertj.core.api.Assertions.assertThat;
.name("name")
.description("desc")
.severity(Severity.BLOCKER)
- .type(RuleType.CODE_SMELL);
- rule.save();
+ .type(RuleType.CODE_SMELL)
+ .characteristic(RuleCharacteristic.COMPLIANT);
+ rule.save();
assertThat(rule.engineId()).isEqualTo("engine");
assertThat(rule.ruleId()).isEqualTo("ruleId");
assertThat(rule.description()).isEqualTo("desc");
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER);
assertThat(rule.type()).isEqualTo(RuleType.CODE_SMELL);
-
+ assertThat(rule.characteristic()).isEqualTo(RuleCharacteristic.COMPLIANT);
verify(storage).store(any(DefaultAdHocRule.class));
}
-
@Test
public void description_is_optional() {
SensorStorage storage = mock(SensorStorage.class);
.hasMessageContaining("Name is mandatory");
}
-
@Test
public void fail_to_store_if_no_severity() {
SensorStorage storage = mock(SensorStorage.class);