Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SONARGO-40 Vulnerability imported through external report Golangci lint should be set to Security in Software Quality #32

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ pluginManagement {
}

dependencyResolutionManagement {
def slangDependenciesVersion = '1.17.0.6351'
//TODO change to released version before merge
def slangDependenciesVersion = '1.18.0.6372'
def analyzerCommonsVersion = '2.16.0.3141'
def pluginApiVersion = '10.10.0.2391'
def sonarqubeVersion = '10.0.0.68432'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
package org.sonar.go.externalreport;

import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleType;
Expand All @@ -47,8 +51,12 @@ public Consumer<File> reportConsumer(SensorContext context) {

private static class GolangCILintCheckstyleFormatImporter extends CheckstyleFormatImporter {

private static final String GOSEC = "gosec";
private final SensorContext context;

public GolangCILintCheckstyleFormatImporter(SensorContext context, String linterKey) {
super(context, linterKey);
this.context = context;
}

/**
Expand All @@ -63,21 +71,34 @@ public GolangCILintCheckstyleFormatImporter(SensorContext context, String linter
*/
@Override
protected RuleType ruleType(@Nullable String severity, String source) {
if ("gosec".equals(source)) {
if (GOSEC.equals(source)) {
return RuleType.VULNERABILITY;
}
return super.ruleType(severity, source);
}

@Override
protected RuleKey createRuleKey(String source, RuleType ruleType, Severity ruleSeverity) {
if ("gosec".equals(source)) {
if (GOSEC.equals(source)) {
// gosec issues are exclusively "major vulnerability", keeping "gosec" as rule key.
return RuleKey.of(linterKey, source);
}
String ruleKey = String.format("%s.%s.%s", source, ruleType.toString().toLowerCase(Locale.ROOT),
ruleSeverity.toString().toLowerCase(Locale.ROOT));
return RuleKey.of(linterKey, ruleKey);
}

@Override
protected List<Impact> impacts(String severity, String source) {
var isSonarCloud = context.runtime().getProduct() == SonarProduct.SONARQUBE && context.runtime().getEdition() == SonarEdition.SONARCLOUD;
if (!isSonarCloud) {
// SonarQube Cloud does not yet support the `impact` field for external issues
if (GOSEC.equals(source)) {
return List.of(new Impact(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM));
}
return List.of(new Impact(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
}
return List.of();
mstachniuk marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito;
import org.slf4j.event.Level;
import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarRuntime;
import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.issue.ExternalIssue;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Version;
import org.sonarsource.slang.testing.ThreadLocalLogTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.when;
import static org.sonar.go.externalreport.ExternalLinterSensorHelper.REPORT_BASE_PATH;

class GolangCILintReportSensorTest {
Expand All @@ -46,7 +54,7 @@ void setup() {
public ThreadLocalLogTester logTester = new ThreadLocalLogTester();

@Test
void test_descriptor() {
void shouldValidateDescriptor() {
DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor();
golangCILintReportSensor().describe(sensorDescriptor);
assertThat(sensorDescriptor.name()).isEqualTo("Import of GolangCI-Lint issues");
Expand All @@ -58,7 +66,7 @@ private GolangCILintReportSensor golangCILintReportSensor() {
}

@Test
void issues_with_sonarqube() throws IOException {
void shouldValidateWithSonarqube() throws IOException {
SensorContextTester context = ExternalLinterSensorHelper.createContext();
context.settings().setProperty("sonar.go.golangci-lint.reportPaths", REPORT_BASE_PATH.resolve("golandci-lint-report.xml").toString());
List<ExternalIssue> externalIssues = ExternalLinterSensorHelper.executeSensor(golangCILintReportSensor(), context);
Expand All @@ -69,6 +77,7 @@ void issues_with_sonarqube() throws IOException {
assertThat(first.severity()).isEqualTo(Severity.MAJOR);
assertThat(first.ruleKey().repository()).isEqualTo("external_golangci-lint");
assertThat(first.ruleKey().rule()).isEqualTo("deadcode.bug.major");
assertThat(first.impacts()).contains(entry(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
assertThat(first.primaryLocation().message()).isEqualTo("`three` is unused");
assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(3);

Expand All @@ -77,6 +86,42 @@ void issues_with_sonarqube() throws IOException {
assertThat(second.severity()).isEqualTo(Severity.MAJOR);
assertThat(second.ruleKey().repository()).isEqualTo("external_golangci-lint");
assertThat(second.ruleKey().rule()).isEqualTo("gosec");
assertThat(second.impacts()).contains(entry(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM));
assertThat(second.primaryLocation().message()).isEqualTo("G402: TLS InsecureSkipVerify set true.");
assertThat(second.primaryLocation().inputComponent().key()).isEqualTo("module:main.go");
assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(4);

assertThat(logTester.logs(Level.ERROR)).isEmpty();
}

@Test
void shouldValidateWithSonarcloud() throws IOException {
SensorContextTester context = ExternalLinterSensorHelper.createContext();
var sonarRuntime = Mockito.mock(SonarRuntime.class);
when(sonarRuntime.getProduct()).thenReturn(SonarProduct.SONARQUBE);
when(sonarRuntime.getEdition()).thenReturn(SonarEdition.SONARCLOUD);
when(sonarRuntime.getApiVersion()).thenReturn(Version.create(7,2));
context.setRuntime(sonarRuntime);
context.settings().setProperty("sonar.go.golangci-lint.reportPaths", REPORT_BASE_PATH.resolve("golandci-lint-report.xml").toString());
List<ExternalIssue> externalIssues = ExternalLinterSensorHelper.executeSensor(golangCILintReportSensor(), context);
assertThat(externalIssues).hasSize(2);

org.sonar.api.batch.sensor.issue.ExternalIssue first = externalIssues.get(0);
assertThat(first.type()).isEqualTo(RuleType.BUG);
assertThat(first.severity()).isEqualTo(Severity.MAJOR);
assertThat(first.ruleKey().repository()).isEqualTo("external_golangci-lint");
assertThat(first.ruleKey().rule()).isEqualTo("deadcode.bug.major");
// For SonarQube Cloud the impact should be empty as it is not supported
assertThat(first.impacts()).isEmpty();
assertThat(first.primaryLocation().message()).isEqualTo("`three` is unused");
assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(3);

ExternalIssue second = externalIssues.get(1);
assertThat(second.type()).isEqualTo(RuleType.VULNERABILITY);
assertThat(second.severity()).isEqualTo(Severity.MAJOR);
assertThat(second.ruleKey().repository()).isEqualTo("external_golangci-lint");
assertThat(second.ruleKey().rule()).isEqualTo("gosec");
assertThat(first.impacts()).isEmpty();
assertThat(second.primaryLocation().message()).isEqualTo("G402: TLS InsecureSkipVerify set true.");
assertThat(second.primaryLocation().inputComponent().key()).isEqualTo("module:main.go");
assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(4);
Expand All @@ -86,7 +131,7 @@ void issues_with_sonarqube() throws IOException {


@Test
void import_check_style_report_same_source_different_key() throws IOException {
void shouldImportSameSourceDifferentKey() throws IOException {
// Check that rules have different key based on the severity
SensorContextTester context = ExternalLinterSensorHelper.createContext();
context.settings().setProperty("sonar.go.golangci-lint.reportPaths", REPORT_BASE_PATH.resolve("checkstyle-different-severity.xml").toString());
Expand Down
Loading