Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ repositories {

defaultTasks('installDist')

version = '0.0.2'
version = '0.0.3'
jar.archiveName = "${jar.baseName}.${jar.extension}"
distZip.archiveName = "${jar.baseName}.zip"

Expand All @@ -65,6 +65,8 @@ dependencies {

implementation 'org.eclipse.jgit:org.eclipse.jgit:5.13.0.202109080827-r'

implementation 'com.contrastsecurity:java-sarif:2.0'

implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
implementation 'org.slf4j:slf4j-nop:2.0.0-alpha5'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.contrastsecurity.sarif.ArtifactLocation;
import com.contrastsecurity.sarif.Location;
import com.contrastsecurity.sarif.Message;
import com.contrastsecurity.sarif.MultiformatMessageString;
import com.contrastsecurity.sarif.PhysicalLocation;
import com.contrastsecurity.sarif.PropertyBag;
import com.contrastsecurity.sarif.Region;
import com.contrastsecurity.sarif.ReportingConfiguration;
import com.contrastsecurity.sarif.ReportingDescriptor;
import com.contrastsecurity.sarif.Result;
import com.contrastsecurity.sarif.Run;
import com.contrastsecurity.sarif.SarifSchema210;
import com.contrastsecurity.sarif.Tool;
import com.contrastsecurity.sarif.ToolComponent;
import lombok.val;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
Expand All @@ -30,6 +49,10 @@ public static void saveResults(final Path outputDir,
val jsonFile = outputDir.resolve("recommendations.json");
JsonUtil.storeRecommendations(results, jsonFile);
Log.info("Recommendations in Json format written to to:%n%s", jsonFile.normalize().toUri());
val sarifFile = outputDir.resolve("recommendations.sarif.json");
JsonUtil.writeSarif(createSarifReport(results), sarifFile);
Log.info("Recommendations in SARIF format written to to:%n%s", sarifFile.normalize().toUri());

createHtmlReport(outputDir, scanMetaData, results);
}

Expand Down Expand Up @@ -119,6 +142,89 @@ private static void createHtmlReport(final Path outputDir,
Log.info("Report with %d recommendations written to:%n%s", validFindings, htmlFile.normalize().toUri());
}

private static SarifSchema210 createSarifReport(final List<RecommendationSummary> recommendations)
throws IOException {
val docUrl = "https://docs.aws.amazon.com/codeguru/latest/reviewer-ug/how-codeguru-reviewer-works.html";

val rulesMap = createSarifRuleDescriptions(recommendations);
val driver = new ToolComponent().withName("CodeGuru Reviewer Scanner")
.withInformationUri(URI.create(docUrl))
.withRules(new HashSet<>(rulesMap.values()));

val results = recommendations.stream().map(ResultsAdapter::convertToSarif)
.collect(Collectors.toList());

val run = new Run().withTool(new Tool().withDriver(driver)).withResults(results);

return new SarifSchema210()
.withVersion(SarifSchema210.Version._2_1_0)
.with$schema(URI.create("http://json.schemastore.org/sarif-2.1.0-rtm.4"))
.withRuns(Arrays.asList(run));

}

private static Map<String, ReportingDescriptor> createSarifRuleDescriptions(
final List<RecommendationSummary> recommendations) {
val rulesMap = new HashMap<String, ReportingDescriptor>();
for (val recommendation : recommendations) {
val metaData = recommendation.ruleMetadata();
if (metaData != null && !rulesMap.containsKey(metaData.ruleId())) {
val properties = new PropertyBag().withTags(new HashSet<>(metaData.ruleTags()));
MultiformatMessageString foo;
val descriptor = new ReportingDescriptor()
.withName(metaData.ruleName())
.withId(metaData.ruleId())
.withShortDescription(new MultiformatMessageString().withText(metaData.ruleName()))
.withFullDescription(new MultiformatMessageString().withText(metaData.shortDescription()))
.withHelp(new MultiformatMessageString().withText(metaData.longDescription()))
.withProperties(properties);
if (recommendation.severityAsString() != null) {
val level = ReportingConfiguration.Level.fromValue(getSarifSeverity(recommendation));
descriptor.setDefaultConfiguration(new ReportingConfiguration().withLevel(level));
}
rulesMap.put(metaData.ruleId(), descriptor);
}
}
return rulesMap;
}

private static Result convertToSarif(final RecommendationSummary recommendation) {
List<Location> locations = Arrays.asList(getSarifLocation(recommendation));
return new Result().withRuleId(recommendation.ruleMetadata().ruleId())
.withLevel(Result.Level.fromValue(getSarifSeverity(recommendation)))
.withMessage(new Message().withMarkdown(recommendation.description()))
.withLocations(locations);
}

private static Location getSarifLocation(final RecommendationSummary recommendation) {
val loc = new PhysicalLocation()
.withArtifactLocation(new ArtifactLocation().withUri(recommendation.filePath()))
.withRegion(new Region().withStartLine(recommendation.startLine())
.withEndLine(recommendation.endLine()));
return new Location()
.withPhysicalLocation(loc);
}

private static String getSarifSeverity(RecommendationSummary recommendation) {
if (recommendation.severity() == null) {
return Result.Level.NONE.value(); // can happen for legacy rules
}
switch (recommendation.severity()) {
case INFO:
return Result.Level.NONE.value();
case LOW:
return Result.Level.NONE.value();
case MEDIUM:
return Result.Level.NONE.value();
case HIGH:
return Result.Level.WARNING.value();
case CRITICAL:
return Result.Level.ERROR.value();
default:
return Result.Level.NONE.value();
}
}

private static void sortByFileName(final List<RecommendationSummary> recommendations) {
Collections.sort(recommendations, (o1, o2) -> {
int pathComp = o1.filePath().compareTo(o2.filePath());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.stream.Collectors;

import com.contrastsecurity.sarif.SarifSchema210;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
Expand Down Expand Up @@ -39,6 +40,11 @@ public static void storeRecommendations(@NonNull final List<RecommendationSummar
OBJECT_MAPPER.writeValue(targetFile.toFile(), recommendations);
}

public static void writeSarif(@NonNull final SarifSchema210 sarif, @NonNull final Path targetFile)
throws IOException {
OBJECT_MAPPER.writeValue(targetFile.toFile(), sarif);
}

private JsonUtil() {
// do not initialize utility
}
Expand Down