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;
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()) {
*/
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 {
issue.setType(rule.getType());
}
}
+ issue.setIsFromHotspot(issue.type() == RuleType.SECURITY_HOTSPOT);
}
}
"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>]");
}
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;
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()
underTest.onIssue(mock(Component.class), issue);
assertThat(issue.type()).isEqualTo(RuleType.BUG);
+ assertThat(issue.isFromHotspot()).isEqualTo(false);
}
@Test
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);
}
}
"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");
private String projectKey;
private String filePath;
private String tags;
+ private boolean isFromHotspot;
/**
* On batch side, component keys and uuid are useless
.setRuleId(ruleId)
.setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
.setExternal(issue.isFromExternalRuleEngine())
+ .setIsFromHotspot(issue.isFromHotspot())
.setTags(issue.tags())
.setComponentUuid(issue.componentUuid())
.setComponentKey(issue.componentKey())
.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())
return this;
}
+ public boolean isFromHotspot() {
+ return isFromHotspot;
+ }
+
+ public IssueDto setIsFromHotspot(boolean value) {
+ isFromHotspot = value;
+ return this;
+ }
+
public String getComponentKey() {
return componentKey;
}
issue.setSelectedAt(selectedAt);
issue.setLocations(parseLocations());
issue.setIsFromExternalRuleEngine(isExternal);
+ issue.setIsFromHotspot(isFromHotspot);
return issue;
}
}
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">
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 <= #{selectedAt}
</update>
--- /dev/null
+/*
+ * 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());
+ }
+
+}
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)
;
}
}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+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");
private Date updateDate;
private Date closeDate;
+ private boolean isFromHotspot = false;
+
// FOLLOWING FIELDS ARE AVAILABLE ONLY DURING SCAN
// Current changes
}
}
+ 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;