]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10868 Add issues.from_hotspot for security hotspots
authorJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Jun 2018 11:20:07 +0000 (13:20 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 4 Jul 2018 07:31:03 +0000 (09:31 +0200)
13 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/RuleTypeCopier.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueAssignerTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/RuleTypeCopierTest.java
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssues.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/AddFromHotspotFlagToIssuesTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssuesTest/issues.sql [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java

index 52855f4234bab0fbdb5a4d529b9e8b75b6870763..c1166f7d55a1dd647441f6ac2286c1f01d324b02 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting;
 import java.util.Date;
 import java.util.Optional;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.RuleType;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueComment;
 import org.sonar.core.issue.FieldDiffs;
@@ -131,6 +132,9 @@ public class IssueLifecycle {
   public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) {
     raw.setKey(base.key());
     raw.setNew(false);
+    if (raw.type() == RuleType.SECURITY_HOTSPOT) {
+      raw.setIsFromHotspot(true);
+    }
     copyFields(raw, base);
 
     if (base.manualSeverity()) {
index 7a6fddb6d72b94600c0816cd11ce5c5eca62ac22..de0a30cd9dabd0c0858a2e53e41be6fa78b491e1 100644 (file)
@@ -19,9 +19,9 @@
  */
 package org.sonar.ce.task.projectanalysis.issue;
 
+import org.sonar.api.rules.RuleType;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.core.issue.DefaultIssue;
-import org.sonar.ce.task.projectanalysis.component.Component;
 
 public class RuleTypeCopier extends IssueVisitor {
 
@@ -40,5 +40,6 @@ public class RuleTypeCopier extends IssueVisitor {
         issue.setType(rule.getType());
       }
     }
+    issue.setIsFromHotspot(issue.type() == RuleType.SECURITY_HOTSPOT);
   }
 }
index 9480f5c53e92d4067d13ef34eac0391daa690da4..ea2593836953de332aee8f40910913d27459a724 100644 (file)
@@ -213,7 +213,7 @@ public class IssueAssignerTest {
         "moduleUuid=<null>,moduleUuidPath=<null>,projectUuid=<null>,projectKey=<null>,ruleKey=<null>,language=<null>,severity=<null>," +
         "manualSeverity=false,message=<null>,line=2,gap=<null>,effort=<null>,status=<null>,resolution=<null>," +
         "assigneeUuid=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,comments=<null>,tags=<null>," +
-        "locations=<null>,isFromExternalRuleEngine=false,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,isCopied=false," +
+        "locations=<null>,isFromExternalRuleEngine=false,creationDate=<null>,updateDate=<null>,closeDate=<null>,isFromHotspot=false,currentChange=<null>,changes=<null>,isNew=true,isCopied=false," +
         "beingClosed=false,onDisabledRule=false,isChanged=false,sendNotifications=false,selectedAt=<null>]");
   }
 
index e76865e0c92fddf0ed671010bf9700b50e9f36d0..23de915307112a10c92b0c0631674eb55214026b 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
 import java.util.Date;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
 import org.sonar.ce.task.projectanalysis.analysis.Branch;
@@ -84,6 +85,38 @@ public class IssueLifecycleTest {
     assertThat(issue.isCopied()).isFalse();
   }
 
+  @Test
+  public void set_fromHotspot_flag_for_existing_vulnerability() {
+    DefaultIssue raw = new DefaultIssue()
+      .setNew(true)
+      .setKey("RAW_KEY")
+      .setType(RuleType.SECURITY_HOTSPOT)
+      .setCreationDate(parseDate("2015-10-01"))
+      .setUpdateDate(parseDate("2015-10-02"))
+      .setCloseDate(parseDate("2015-10-03"));
+
+    DefaultIssue base = new DefaultIssue()
+      .setKey("BASE_KEY")
+      .setType(RuleType.VULNERABILITY)
+      .setResolution(RESOLUTION_FIXED)
+      .setStatus(STATUS_CLOSED)
+      .setSeverity(BLOCKER)
+      .setCreationDate(parseDate("2015-01-01"))
+      .setUpdateDate(parseDate("2015-01-02"))
+      .setCloseDate(parseDate("2015-01-03"));
+
+    underTest.mergeExistingOpenIssue(raw, base);
+
+    assertThat(raw.isNew()).isFalse();
+    assertThat(raw.key()).isNotNull();
+    assertThat(raw.key()).isEqualTo(base.key());
+    assertThat(raw.creationDate()).isEqualTo(base.creationDate());
+    assertThat(raw.updateDate()).isEqualTo(base.updateDate());
+    assertThat(raw.closeDate()).isEqualTo(base.closeDate());
+    assertThat(raw.type()).isEqualTo(RuleType.VULNERABILITY);
+    assertThat(raw.isFromHotspot()).isTrue();
+  }
+
   @Test
   public void mergeIssueFromShortLivingBranch() {
     DefaultIssue raw = new DefaultIssue()
index 906a15cd4e3ba6e778e2e5f77316c151d7ec3931..f29f69f37558fedb30202115fc6a3fc84b249b1a 100644 (file)
@@ -45,6 +45,7 @@ public class RuleTypeCopierTest {
     underTest.onIssue(mock(Component.class), issue);
 
     assertThat(issue.type()).isEqualTo(RuleType.BUG);
+    assertThat(issue.isFromHotspot()).isEqualTo(false);
   }
 
   @Test
@@ -55,5 +56,16 @@ public class RuleTypeCopierTest {
     underTest.onIssue(mock(Component.class), issue);
 
     assertThat(issue.type()).isEqualTo(RuleType.VULNERABILITY);
+    assertThat(issue.isFromHotspot()).isEqualTo(false);
+  }
+
+  @Test
+  public void set_from_hotspot_flag_for_hotspot() {
+    rule.setType(RuleType.SECURITY_HOTSPOT);
+
+    underTest.onIssue(mock(Component.class), issue);
+
+    assertThat(issue.type()).isEqualTo(RuleType.SECURITY_HOTSPOT);
+    assertThat(issue.isFromHotspot()).isEqualTo(true);
   }
 }
index c9ae525b88d73915351ddfdc80ae0c32863aa55d..628f2af41db94e420bcee8c2866bac7e3dd8a655 100644 (file)
@@ -552,7 +552,8 @@ CREATE TABLE "ISSUES" (
   "CREATED_AT" BIGINT,
   "UPDATED_AT" BIGINT,
   "LOCATIONS" BLOB,
-  "ISSUE_TYPE" TINYINT
+  "ISSUE_TYPE" TINYINT,
+  "FROM_HOTSPOT" BOOLEAN NULL
 );
 CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
 CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID");
index 0ec0627178fff71ce8d915524364e5aa77d3f12c..df8b2f9fb0706644711aa686eb37f5f0e6db8de7 100644 (file)
@@ -96,6 +96,7 @@ public final class IssueDto implements Serializable {
   private String projectKey;
   private String filePath;
   private String tags;
+  private boolean isFromHotspot;
 
   /**
    * On batch side, component keys and uuid are useless
@@ -118,6 +119,7 @@ public final class IssueDto implements Serializable {
       .setRuleId(ruleId)
       .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
       .setExternal(issue.isFromExternalRuleEngine())
+      .setIsFromHotspot(issue.isFromHotspot())
       .setTags(issue.tags())
       .setComponentUuid(issue.componentUuid())
       .setComponentKey(issue.componentKey())
@@ -166,6 +168,7 @@ public final class IssueDto implements Serializable {
       .setAuthorLogin(issue.authorLogin())
       .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
       .setExternal(issue.isFromExternalRuleEngine())
+      .setIsFromHotspot(issue.isFromHotspot())
       .setTags(issue.tags())
       .setComponentUuid(issue.componentUuid())
       .setComponentKey(issue.componentKey())
@@ -483,6 +486,15 @@ public final class IssueDto implements Serializable {
     return this;
   }
 
+  public boolean isFromHotspot() {
+    return isFromHotspot;
+  }
+
+  public IssueDto setIsFromHotspot(boolean value) {
+    isFromHotspot = value;
+    return this;
+  }
+
   public String getComponentKey() {
     return componentKey;
   }
@@ -723,6 +735,7 @@ public final class IssueDto implements Serializable {
     issue.setSelectedAt(selectedAt);
     issue.setLocations(parseLocations());
     issue.setIsFromExternalRuleEngine(isExternal);
+    issue.setIsFromHotspot(isFromHotspot);
     return issue;
   }
 }
index 122f92b49447db84b100765ac1386bc84fda749f..63468133dd1d81828206737872f22e3afac11279 100644 (file)
@@ -38,7 +38,8 @@
     p.path as filePath,
     root.kee as projectKey,
     i.project_uuid as projectUuid,
-    i.issue_type as type
+    i.issue_type as type,
+    i.from_hotspot as "isFromHotspot"
   </sql>
 
   <sql id="sortColumn">
@@ -95,7 +96,8 @@
     p.scope,
     p.organization_uuid as "organizationUuid",
     i.tags,
-    i.issue_type as "issueType"
+    i.issue_type as "issueType",
+    i.from_hotspot as "isFromHotspot"
   </sql>
 
 
     INSERT INTO issues (kee, rule_id, severity, manual_severity,
     message, line, locations, gap, effort, status, tags,
     resolution, checksum, assignee, author_login, issue_attributes, issue_creation_date, issue_update_date,
-    issue_close_date, created_at, updated_at, component_uuid, project_uuid, issue_type)
+    issue_close_date, created_at, updated_at, component_uuid, project_uuid, issue_type, from_hotspot)
     VALUES (#{kee,jdbcType=VARCHAR}, #{ruleId,jdbcType=INTEGER},
     #{severity,jdbcType=VARCHAR},
     #{manualSeverity,jdbcType=BOOLEAN}, #{message,jdbcType=VARCHAR}, #{line,jdbcType=INTEGER},
     #{issueAttributes,jdbcType=VARCHAR},
     #{issueCreationTime,jdbcType=BIGINT},#{issueUpdateTime,jdbcType=BIGINT}, #{issueCloseTime,jdbcType=BIGINT},
     #{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT},
-    #{componentUuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}, #{type,jdbcType=INTEGER})
+    #{componentUuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}, #{type,jdbcType=INTEGER}, #{isFromHotspot,jdbcType=BOOLEAN})
   </insert>
 
   <!--
     issue_update_date=#{issueUpdateTime,jdbcType=BIGINT},
     issue_close_date=#{issueCloseTime,jdbcType=BIGINT},
     updated_at=#{updatedAt,jdbcType=BIGINT},
-    issue_type=#{type,jdbcType=INTEGER}
+    issue_type=#{type,jdbcType=INTEGER},
+    from_hotspot=#{isFromHotspot,jdbcType=BOOLEAN}
     where kee = #{kee}
   </update>
 
     issue_update_date=#{issueUpdateTime,jdbcType=BIGINT},
     issue_close_date=#{issueCloseTime,jdbcType=BIGINT},
     updated_at=#{updatedAt,jdbcType=BIGINT},
-    issue_type=#{type,jdbcType=INTEGER}
+    issue_type=#{type,jdbcType=INTEGER},
+    from_hotspot=#{isFromHotspot,jdbcType=BOOLEAN}
     where kee = #{kee} and updated_at &lt;= #{selectedAt}
   </update>
 
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssues.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssues.java
new file mode 100644 (file)
index 0000000..c7262d1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddFromHotspotFlagToIssues extends DdlChange {
+
+  public AddFromHotspotFlagToIssues(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AddColumnsBuilder(getDialect(), "issues")
+      .addColumn(newBooleanColumnDefBuilder()
+        .setColumnName("from_hotspot")
+        .setIsNullable(true)
+        .build())
+      .build());
+  }
+
+}
index 88bdf217071af6daa819691b642fb6fa7a2f0660..603577df4978ed1915557d8f7c46e70c72bf1dd2 100644 (file)
@@ -28,6 +28,7 @@ public class DbVersion73 implements DbVersion {
   public void addSteps(MigrationStepRegistry registry) {
     registry
       .add(2200, "Populate PROJECT_BRANCHES with existing main application branches", PopulateMainApplicationBranches.class)
+      .add(2201, "Add 'from hotspot' flag to issues", AddFromHotspotFlagToIssues.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssuesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssuesTest.java
new file mode 100644 (file)
index 0000000..fde90d9
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 static java.sql.Types.BOOLEAN;
+
+public class AddFromHotspotFlagToIssuesTest {
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(AddFromHotspotFlagToIssuesTest.class, "issues.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private AddFromHotspotFlagToIssues underTest = new AddFromHotspotFlagToIssues(db.database());
+
+  @Test
+  public void column_is_added_to_table() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDefinition("issues", "from_hotspot", BOOLEAN, null, 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/AddFromHotspotFlagToIssuesTest/issues.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v73/AddFromHotspotFlagToIssuesTest/issues.sql
new file mode 100644 (file)
index 0000000..af9aacd
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE TABLE "ISSUES" (
+  "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "KEE" VARCHAR(50) UNIQUE NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50),
+  "PROJECT_UUID" VARCHAR(50),
+  "RULE_ID" INTEGER,
+  "SEVERITY" VARCHAR(10),
+  "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+  "MESSAGE" VARCHAR(4000),
+  "LINE" INTEGER,
+  "GAP" DOUBLE,
+  "EFFORT" INTEGER,
+  "STATUS" VARCHAR(20),
+  "RESOLUTION" VARCHAR(20),
+  "CHECKSUM" VARCHAR(1000),
+  "REPORTER" VARCHAR(255),
+  "ASSIGNEE" VARCHAR(255),
+  "AUTHOR_LOGIN" VARCHAR(255),
+  "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+  "ISSUE_ATTRIBUTES" VARCHAR(4000),
+  "TAGS" VARCHAR(4000),
+  "ISSUE_CREATION_DATE" BIGINT,
+  "ISSUE_CLOSE_DATE" BIGINT,
+  "ISSUE_UPDATE_DATE" BIGINT,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT,
+  "LOCATIONS" BLOB,
+  "ISSUE_TYPE" TINYINT
+);
+CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
+CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID");
+CREATE INDEX "ISSUES_PROJECT_UUID" ON "ISSUES" ("PROJECT_UUID");
+CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID");
+CREATE INDEX "ISSUES_RESOLUTION" ON "ISSUES" ("RESOLUTION");
+CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES" ("ASSIGNEE");
+CREATE INDEX "ISSUES_CREATION_DATE" ON "ISSUES" ("ISSUE_CREATION_DATE");
+CREATE INDEX "ISSUES_UPDATED_AT" ON "ISSUES" ("UPDATED_AT");
index 04513564a0363862a45b97819877213eef803d9b..125c9bd51e6d259be3f009f8feeed1b3da521394 100644 (file)
@@ -88,6 +88,8 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
   private Date updateDate;
   private Date closeDate;
 
+  private boolean isFromHotspot = false;
+
   // FOLLOWING FIELDS ARE AVAILABLE ONLY DURING SCAN
 
   // Current changes
@@ -606,6 +608,15 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
     }
   }
 
+  public DefaultIssue setIsFromHotspot(boolean value) {
+    this.isFromHotspot = value;
+    return this;
+  }
+
+  public boolean isFromHotspot() {
+    return isFromHotspot;
+  }
+
   public DefaultIssue setTags(Collection<String> tags) {
     this.tags = new LinkedHashSet<>(tags);
     return this;