@@ -212,6 +212,7 @@ CREATE TABLE "RULES" ( | |||
"DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20), | |||
"GAP_DESCRIPTION" VARCHAR(4000), | |||
"SYSTEM_TAGS" VARCHAR(4000), | |||
"SECURITY_STANDARDS" VARCHAR(4000), | |||
"RULE_TYPE" TINYINT, | |||
"CREATED_AT" BIGINT, | |||
"UPDATED_AT" BIGINT |
@@ -55,6 +55,7 @@ public class RuleDefinitionDto { | |||
private String defRemediationBaseEffort; | |||
private String gapDescription; | |||
private String systemTags; | |||
private String securityStandards; | |||
private int type; | |||
private Scope scope; | |||
@@ -277,6 +278,23 @@ public class RuleDefinitionDto { | |||
return this; | |||
} | |||
public Set<String> getSecurityStandards() { | |||
return securityStandards == null ? new HashSet<>() : new TreeSet<>(Arrays.asList(StringUtils.split(securityStandards, ','))); | |||
} | |||
private String getSecurityStandardsField() { | |||
return securityStandards; | |||
} | |||
void setSecurityStandardsField(String s) { | |||
securityStandards = s; | |||
} | |||
public RuleDefinitionDto setSecurityStandards(Set<String> standards) { | |||
this.securityStandards = standards.isEmpty() ? null : StringUtils.join(standards, ','); | |||
return this; | |||
} | |||
public int getType() { | |||
return type; | |||
} |
@@ -260,6 +260,11 @@ public class RuleDto { | |||
return this; | |||
} | |||
public RuleDto setSecurityStandards(Set<String> standards) { | |||
this.definition.setSecurityStandards(standards); | |||
return this; | |||
} | |||
public int getType() { | |||
return definition.getType(); | |||
} | |||
@@ -285,6 +290,17 @@ public class RuleDto { | |||
definition.setSystemTagsField(s); | |||
} | |||
public Set<String> getSecurityStandards() { | |||
return definition.getSecurityStandards(); | |||
} | |||
/** | |||
* Used in MyBatis mapping. | |||
*/ | |||
private void setSecurityStandardsField(String s) { | |||
definition.setSecurityStandardsField(s); | |||
} | |||
public long getCreatedAt() { | |||
return definition.getCreatedAt(); | |||
} |
@@ -39,6 +39,7 @@ public class RuleForIndexingDto { | |||
private RuleStatus status; | |||
private boolean isTemplate; | |||
private String systemTags; | |||
private String securityStandards; | |||
private String templateRuleKey; | |||
private String templateRepository; | |||
private String internalKey; | |||
@@ -49,6 +50,7 @@ public class RuleForIndexingDto { | |||
private long updatedAt; | |||
private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | |||
private static final Splitter SECURITY_STANDARDS_SPLITTER = TAGS_SPLITTER; | |||
public Integer getId() { | |||
return id; | |||
@@ -90,6 +92,10 @@ public class RuleForIndexingDto { | |||
return systemTags; | |||
} | |||
public String getSecurityStandards() { | |||
return securityStandards; | |||
} | |||
public String getTemplateRuleKey() { | |||
return templateRuleKey; | |||
} | |||
@@ -138,4 +144,8 @@ public class RuleForIndexingDto { | |||
public Set<String> getSystemTagsAsSet() { | |||
return ImmutableSet.copyOf(TAGS_SPLITTER.split(systemTags == null ? "" : systemTags)); | |||
} | |||
public Set<String> getSecurityStandardsAsSet() { | |||
return ImmutableSet.copyOf(SECURITY_STANDARDS_SPLITTER.split(securityStandards == null ? "" : securityStandards)); | |||
} | |||
} |
@@ -22,6 +22,7 @@ | |||
r.def_remediation_base_effort as "defRemediationBaseEffort", | |||
r.gap_description as "gapDescription", | |||
r.system_tags as "systemTagsField", | |||
r.security_standards as "securityStandardsField", | |||
r.rule_type as "type", | |||
r.plugin_key as "pluginKey", | |||
r.scope, | |||
@@ -253,6 +254,7 @@ | |||
r.is_template as "isTemplate", | |||
r.is_external as "isExternal", | |||
r.system_tags as "systemTags", | |||
r.security_standards as "securityStandards", | |||
t.plugin_rule_key as "templateRuleKey", | |||
t.plugin_name as "templateRepository", | |||
r.plugin_config_key as "internalKey", | |||
@@ -305,6 +307,7 @@ | |||
def_remediation_base_effort, | |||
gap_description, | |||
system_tags, | |||
security_standards, | |||
rule_type, | |||
scope, | |||
created_at, | |||
@@ -329,6 +332,7 @@ | |||
#{defRemediationBaseEffort,jdbcType=VARCHAR}, | |||
#{gapDescription,jdbcType=VARCHAR}, | |||
#{systemTagsField,jdbcType=VARCHAR}, | |||
#{securityStandardsField,jdbcType=VARCHAR}, | |||
#{type,jdbcType=TINYINT}, | |||
#{scope,jdbcType=VARCHAR}, | |||
#{createdAt,jdbcType=BIGINT}, | |||
@@ -356,6 +360,7 @@ | |||
def_remediation_base_effort=#{defRemediationBaseEffort,jdbcType=VARCHAR}, | |||
gap_description=#{gapDescription,jdbcType=VARCHAR}, | |||
system_tags=#{systemTagsField,jdbcType=VARCHAR}, | |||
security_standards=#{securityStandardsField,jdbcType=VARCHAR}, | |||
scope=#{scope,jdbcType=VARCHAR}, | |||
rule_type=#{type,jdbcType=TINYINT}, | |||
updated_at=#{updatedAt,jdbcType=BIGINT} |
@@ -370,6 +370,7 @@ public class RuleDaoTest { | |||
assertThat(actual.getDefRemediationBaseEffort()).isEqualTo(expected.getDefRemediationBaseEffort()); | |||
assertThat(actual.getGapDescription()).isEqualTo(expected.getGapDescription()); | |||
assertThat(actual.getSystemTags()).isEqualTo(expected.getSystemTags()); | |||
assertThat(actual.getSecurityStandards()).isEqualTo(expected.getSecurityStandards()); | |||
assertThat(actual.getType()).isEqualTo(expected.getType()); | |||
assertThat(actual.getCreatedAt()).isEqualTo(expected.getCreatedAt()); | |||
assertThat(actual.getUpdatedAt()).isEqualTo(expected.getUpdatedAt()); | |||
@@ -490,6 +491,7 @@ public class RuleDaoTest { | |||
.setDefRemediationBaseEffort("10h") | |||
.setGapDescription("squid.S115.effortToFix") | |||
.setSystemTags(newHashSet("systag1", "systag2")) | |||
.setSecurityStandards(newHashSet("owaspTop10:a1", "cwe:123")) | |||
.setType(RuleType.BUG) | |||
.setScope(Scope.ALL) | |||
.setCreatedAt(1_500_000_000_000L) | |||
@@ -516,6 +518,7 @@ public class RuleDaoTest { | |||
assertThat(ruleDto.getDefRemediationBaseEffort()).isEqualTo("10h"); | |||
assertThat(ruleDto.getGapDescription()).isEqualTo("squid.S115.effortToFix"); | |||
assertThat(ruleDto.getSystemTags()).containsOnly("systag1", "systag2"); | |||
assertThat(ruleDto.getSecurityStandards()).containsOnly("owaspTop10:a1", "cwe:123"); | |||
assertThat(ruleDto.getScope()).isEqualTo(Scope.ALL); | |||
assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant()); | |||
assertThat(ruleDto.getCreatedAt()).isEqualTo(1_500_000_000_000L); | |||
@@ -544,6 +547,7 @@ public class RuleDaoTest { | |||
.setDefRemediationBaseEffort("10h") | |||
.setGapDescription("squid.S115.effortToFix") | |||
.setSystemTags(newHashSet("systag1", "systag2")) | |||
.setSecurityStandards(newHashSet("owaspTop10:a1", "cwe:123")) | |||
.setScope(Scope.ALL) | |||
.setType(RuleType.BUG) | |||
.setUpdatedAt(2_000_000_000_000L); | |||
@@ -569,6 +573,7 @@ public class RuleDaoTest { | |||
assertThat(ruleDto.getDefRemediationBaseEffort()).isEqualTo("10h"); | |||
assertThat(ruleDto.getGapDescription()).isEqualTo("squid.S115.effortToFix"); | |||
assertThat(ruleDto.getSystemTags()).containsOnly("systag1", "systag2"); | |||
assertThat(ruleDto.getSecurityStandards()).containsOnly("owaspTop10:a1", "cwe:123"); | |||
assertThat(ruleDto.getScope()).isEqualTo(Scope.ALL); | |||
assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant()); | |||
assertThat(ruleDto.getCreatedAt()).isEqualTo(1_500_000_000_000L); | |||
@@ -623,6 +628,7 @@ public class RuleDaoTest { | |||
assertThat(ruleDto.getGapDescription()).isNull(); | |||
assertThat(ruleDto.getTags()).containsOnly("tag1", "tag2"); | |||
assertThat(ruleDto.getSystemTags()).isEmpty(); | |||
assertThat(ruleDto.getSecurityStandards()).isEmpty(); | |||
assertThat(ruleDto.getType()).isEqualTo(0); | |||
assertThat(ruleDto.getCreatedAt()).isEqualTo(3_500_000_000_000L); | |||
assertThat(ruleDto.getUpdatedAt()).isEqualTo(4_000_000_000_000L); | |||
@@ -681,6 +687,7 @@ public class RuleDaoTest { | |||
assertThat(ruleDto.getGapDescription()).isNull(); | |||
assertThat(ruleDto.getTags()).isEmpty(); | |||
assertThat(ruleDto.getSystemTags()).isEmpty(); | |||
assertThat(ruleDto.getSecurityStandards()).isEmpty(); | |||
assertThat(ruleDto.getType()).isEqualTo(0); | |||
assertThat(ruleDto.getCreatedAt()).isEqualTo(3_500_000_000_000L); | |||
assertThat(ruleDto.getUpdatedAt()).isEqualTo(4_000_000_000_000L); | |||
@@ -713,6 +720,7 @@ public class RuleDaoTest { | |||
assertThat(ruleDto.getGapDescription()).isNull(); | |||
assertThat(ruleDto.getTags()).containsOnly("tag1", "tag2"); | |||
assertThat(ruleDto.getSystemTags()).isEmpty(); | |||
assertThat(ruleDto.getSecurityStandards()).isEmpty(); | |||
assertThat(ruleDto.getType()).isEqualTo(0); | |||
assertThat(ruleDto.getCreatedAt()).isEqualTo(3_500_000_000_000L); | |||
assertThat(ruleDto.getUpdatedAt()).isEqualTo(7_000_000_000_000L); | |||
@@ -848,6 +856,7 @@ public class RuleDaoTest { | |||
assertThat(firstRule.isExternal()).isFalse(); | |||
assertThat(firstRule.isTemplate()).isEqualTo(r1.isTemplate()); | |||
assertThat(firstRule.getSystemTagsAsSet()).isEqualTo(r1.getSystemTags()); | |||
assertThat(firstRule.getSecurityStandardsAsSet()).isEqualTo(r1.getSecurityStandards()); | |||
assertThat(firstRule.getTemplateRuleKey()).isNull(); | |||
assertThat(firstRule.getTemplateRepository()).isNull(); | |||
assertThat(firstRule.getInternalKey()).isEqualTo(r1.getConfigKey()); | |||
@@ -927,6 +936,7 @@ public class RuleDaoTest { | |||
assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus()); | |||
assertThat(firstRule.isTemplate()).isEqualTo(r1.isTemplate()); | |||
assertThat(firstRule.getSystemTagsAsSet()).isEqualTo(r1.getSystemTags()); | |||
assertThat(firstRule.getSecurityStandardsAsSet()).isEqualTo(r1.getSecurityStandards()); | |||
assertThat(firstRule.getInternalKey()).isEqualTo(r1.getConfigKey()); | |||
assertThat(firstRule.getLanguage()).isEqualTo(r1.getLanguage()); | |||
assertThat(firstRule.getType()).isEqualTo(r1.getType()); |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.v73; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.SupportsBlueGreen; | |||
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; | |||
@SupportsBlueGreen | |||
public class AddSecurityStandardsToRules extends DdlChange { | |||
private static final String TABLE_NAME = "rules"; | |||
private static final String COLUMN_NAME = "security_standards"; | |||
private static final int COLUMN_LENGTH = 4000; | |||
public AddSecurityStandardsToRules(Database db) { | |||
super(db); | |||
} | |||
@Override public void execute(Context context) throws SQLException { | |||
context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) | |||
.addColumn(VarcharColumnDef.newVarcharColumnDefBuilder() | |||
.setColumnName(COLUMN_NAME) | |||
.setIsNullable(true) | |||
.setLimit(COLUMN_LENGTH) | |||
.build()) | |||
.build()); | |||
} | |||
} |
@@ -35,6 +35,7 @@ public class DbVersion73 implements DbVersion { | |||
.add(2205, "Add 'from hotspot' flag to issues", AddFromHotspotFlagToIssues.class) | |||
.add(2206, "Add SUBSCRIPTION column to ORGANIZATIONS table", AddSubscriptionToOrganizations.class) | |||
.add(2207, "Populate SUBSCRIPTION in ORGANIZATIONS", PopulateSubscriptionOnOrganizations.class) | |||
.add(2208, "Add rules.security_standards", AddSecurityStandardsToRules.class) | |||
; | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.v73; | |||
import java.sql.SQLException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.CoreDbTester; | |||
import org.sonar.server.platform.db.migration.version.v72.AddOrganizationUuidToUsers; | |||
import static java.sql.Types.VARCHAR; | |||
public class AddSecurityStandardsToRulesTest { | |||
@Rule | |||
public final CoreDbTester db = CoreDbTester.createForSchema(AddSecurityStandardsToRulesTest.class, "rules.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private AddSecurityStandardsToRules underTest = new AddSecurityStandardsToRules(db.database()); | |||
@Test | |||
public void column_is_added_to_table() throws SQLException { | |||
underTest.execute(); | |||
db.assertColumnDefinition("rules", "security_standards", VARCHAR, 4000, true); | |||
} | |||
@Test | |||
public void migration_is_not_reentrant() throws SQLException { | |||
underTest.execute(); | |||
expectedException.expect(IllegalStateException.class); | |||
underTest.execute(); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
CREATE TABLE "RULES" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"PLUGIN_KEY" VARCHAR(200), | |||
"PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL, | |||
"PLUGIN_NAME" VARCHAR(255) NOT NULL, | |||
"DESCRIPTION" VARCHAR(16777215), | |||
"DESCRIPTION_FORMAT" VARCHAR(20), | |||
"PRIORITY" INTEGER, | |||
"IS_TEMPLATE" BOOLEAN DEFAULT FALSE, | |||
"IS_EXTERNAL" BOOLEAN, | |||
"TEMPLATE_ID" INTEGER, | |||
"PLUGIN_CONFIG_KEY" VARCHAR(200), | |||
"NAME" VARCHAR(200), | |||
"STATUS" VARCHAR(40), | |||
"LANGUAGE" VARCHAR(20), | |||
"SCOPE" VARCHAR(20) NOT NULL, | |||
"DEF_REMEDIATION_FUNCTION" VARCHAR(20), | |||
"DEF_REMEDIATION_GAP_MULT" VARCHAR(20), | |||
"DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20), | |||
"GAP_DESCRIPTION" VARCHAR(4000), | |||
"SYSTEM_TAGS" VARCHAR(4000), | |||
"RULE_TYPE" TINYINT, | |||
"CREATED_AT" BIGINT, | |||
"UPDATED_AT" BIGINT | |||
); | |||
CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY"); |
@@ -170,6 +170,7 @@ public class RuleCreator { | |||
.setGapDescription(templateRuleDto.getGapDescription()) | |||
.setScope(templateRuleDto.getScope()) | |||
.setSystemTags(templateRuleDto.getSystemTags()) | |||
.setSecurityStandards(templateRuleDto.getSecurityStandards()) | |||
.setCreatedAt(system2.now()) | |||
.setUpdatedAt(system2.now()); | |||
dbClient.ruleDao().insert(dbSession, ruleDefinition); |
@@ -103,6 +103,7 @@ public class RuleCreatorTest { | |||
assertThat(rule.getGapDescription()).isEqualTo("desc"); | |||
assertThat(rule.getTags()).containsOnly("usertag1", "usertag2"); | |||
assertThat(rule.getSystemTags()).containsOnly("tag1", "tag4"); | |||
assertThat(rule.getSecurityStandards()).containsOnly("owaspTop10:a1", "cwe:123"); | |||
List<RuleParamDto> params = dbTester.getDbClient().ruleDao().selectRuleParamsByRuleKey(dbSession, customRuleKey); | |||
assertThat(params).hasSize(1); | |||
@@ -468,6 +469,7 @@ public class RuleCreatorTest { | |||
.setGapDescription("desc") | |||
.setTags(Sets.newHashSet("usertag1", "usertag2")) | |||
.setSystemTags(Sets.newHashSet("tag1", "tag4")) | |||
.setSecurityStandards(Sets.newHashSet("owaspTop10:a1", "cwe:123")) | |||
.setCreatedAt(new Date().getTime()) | |||
.setUpdatedAt(new Date().getTime()); | |||
dbTester.rules().insert(templateRule.getDefinition()); |