]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10986 Extend RulesDefinitions API to support security standards (#466)
authorJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 3 Jul 2018 11:16:03 +0000 (13:16 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 17 Jul 2018 18:21:23 +0000 (20:21 +0200)
12 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleForIndexingDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRules.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/DbVersion73.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest/rules.sql [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java
server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorTest.java

index 929a88066f921215210b06c13bb4727bbaadf045..2285d106cca8f233a4a817bec3740a225b6504d9 100644 (file)
@@ -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
index 4ab077e023b10e9f61cc84fbe02bf76a096fec72..9eeaccf681a85a22e5e209fba083f85ceca285b2 100644 (file)
@@ -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;
   }
index 3441baff04d2c6e3617a3bc6d729947f5b081c9a..6fe07bcfb6e5d1f004d0266dc4e50c68f0c2bba4 100644 (file)
@@ -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();
   }
index e76a891a819c6aeb668918e9a061cd9b04e607d5..427e7ba125a02dcba4b3dec823ebb0b040be4513 100644 (file)
@@ -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));
+  }
 }
index 487a02f4e0870b0175719ddf4e6f5b74470fed0d..12957844f6b408013cbf3290e73b32cb48b58d6b 100644 (file)
@@ -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,
       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",
       def_remediation_base_effort,
       gap_description,
       system_tags,
+      security_standards,
       rule_type,
       scope,
       created_at,
       #{defRemediationBaseEffort,jdbcType=VARCHAR},
       #{gapDescription,jdbcType=VARCHAR},
       #{systemTagsField,jdbcType=VARCHAR},
+      #{securityStandardsField,jdbcType=VARCHAR},
       #{type,jdbcType=TINYINT},
       #{scope,jdbcType=VARCHAR},
       #{createdAt,jdbcType=BIGINT},
       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}
index a0ae3743e20aa10b942c04619925ca0d85eb8c31..8020ec5766313dafc98bd5d1b461914b6574e3ff 100644 (file)
@@ -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());
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRules.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRules.java
new file mode 100644 (file)
index 0000000..2eecc0e
--- /dev/null
@@ -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());
+  }
+}
index f149eb7ceafe1e3b7ed75d93f63caadf72bc9ecb..2f0fbdf88caaa586c54fe2bef202cf0341d42964 100644 (file)
@@ -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)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest.java
new file mode 100644 (file)
index 0000000..7e8f44f
--- /dev/null
@@ -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();
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest/rules.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/AddSecurityStandardsToRulesTest/rules.sql
new file mode 100644 (file)
index 0000000..c1718d4
--- /dev/null
@@ -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");
index b6823f0e3ef6d00d7ef20ca9ef8af9c161e9da00..78b157c4eb099b0955d66c902b5aa482648948fb 100644 (file)
@@ -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);
index e09db08a604d30b1be7b792bf1fe944ffb4085d8..4dff170b40e8d031180d2f68de27c3b4399cb6d9 100644 (file)
@@ -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());