Skip to content

Commit

Permalink
Dynamically Fetching Trivy Binary (#41)
Browse files Browse the repository at this point in the history
feat: adds the ability to choose the version of Trivy and download and configure the binary during the runtime of the plugin
  • Loading branch information
netodevel-orla authored Mar 21, 2024
1 parent c15b953 commit 4f29650
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM eclipse-temurin:19.0.2_7-jre-ubi9-minimal
FROM eclipse-temurin:8u402-b06-jre-alpine

ADD target/*.jar /opt/app.jar
CMD ["java", "-jar", "/opt/app.jar"]
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Add the Maven plugin to your project's pom.xml file, specifying the required con
<vulnType>os,library</vulnType>
<severity>HIGH,CRITICAL</severity>
<ignoreUnfixed>true</ignoreUnfixed>
<trivyVersion>v0.49.1</trivyVersion>
</configuration>
<executions>
<execution>
Expand All @@ -52,6 +53,7 @@ The plugin provides the following basic configuration:
* `vulnType`: vulnerability types to be analyzed. Default: all vulnerabilities.
* `severity`: minimum severity of vulnerabilities to be displayed. Default: all severities.
* `ignoreUnfixed`: ignore unfixed vulnerabilities. Default: false.
* `trivyVersion`: choose trivy version. Default: v0.49.1.

## Contributing
Contributions are welcome! Feel free to fork this repository, create a branch, make the desired changes, and submit a pull request.
Expand Down
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
<artifactId>guava</artifactId>
<version>32.1.1-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.47</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
122 changes: 113 additions & 9 deletions src/main/java/br/com/orla/TrivyProcess.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,131 @@
package br.com.orla;

import br.com.orla.api.GithubTrivyReleaseApi;
import br.com.orla.utils.OS;
import br.com.orla.utils.OSDetector;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.codehaus.plexus.util.FileUtils;

public class TrivyProcess extends AbstractOSProcess {

public String getLocationTrivyBin() throws Exception {
public static final Logger LOG = Logger.getLogger(TrivyProcess.class.getName());

private final GithubTrivyReleaseApi githubTrivyReleaseApi;

public TrivyProcess(GithubTrivyReleaseApi githubTrivyReleaseApi) {
this.githubTrivyReleaseApi = githubTrivyReleaseApi;
}

public String resolveBinaryName(String tag) {
var os = OSDetector.getOS();
var pattern = "trivy_%s_%s-%sbit.tar.gz";
if (os.equals(OS.UNIX)) {
return "trivy_UNIX_X86_64";
return String.format(pattern, tag.substring(1), "Linux", "64");
}
if (os.equals(OS.WINDOWS)) {
return "trivy_WINDOWS_X86_64.exe";
return String.format(pattern, tag.substring(1), "Windows", "64");
}
if (os.equals(OS.MAC_OSX)) {
return "trivy_MACOS_64";
return String.format(pattern, tag.substring(1), "macOS", "64");
}
return "";
}

public Path downloadBinaryFromGithubAssets(String downloadUrl, String file) {
try {
var url = new URL(downloadUrl);
var inputStream = url.openStream();
var targetDirectoryPath = Paths.get("").toAbsolutePath() + "/target";

String targetFilePath = targetDirectoryPath + "/" + file;
try (FileOutputStream outputStream = new FileOutputStream(targetFilePath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
inputStream.close();
return Path.of(targetFilePath);
} catch (Exception e) {
LOG.info("error download trivy binary. Error: ".concat(e.getMessage()));
System.out.println("error download trivy binary".concat(e.getMessage()));
}
throw new RuntimeException("error download binary trivy");
}

public Path decompressTarGz(Path pathFile) {
try {
var source = java.nio.file.Files.newInputStream(pathFile);
var gzip = new GZIPInputStream(source);
TarArchiveInputStream tar = new TarArchiveInputStream(gzip);
TarArchiveEntry entry;

while ((entry = tar.getNextTarEntry()) != null) {
if (entry.getName().equals("trivy")) {
var targetDirectoryPath = Paths.get("").toAbsolutePath() + "/target";
String binPath = targetDirectoryPath + "/trivy";

try (OutputStream fileOutput = java.nio.file.Files.newOutputStream(Path.of(binPath));
BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutput)) {
byte[] buffer = new byte[1024];
int read;
while ((read = tar.read(buffer)) != -1) {
bufferedOutput.write(buffer, 0, read);
}
return Path.of(binPath);
}
}
}
} catch (Exception e) {
LOG.info("decompress tar.gz failed. Error = " + e.getMessage());
throw new RuntimeException(e);
}
throw new RuntimeException("decompress tar gz failed");
}

public File getLocationTrivyBin(String trivyTag) throws Exception {
var fileAlreadyExists = getBinFileIfAlreadyExists();
if (fileAlreadyExists != null) return fileAlreadyExists;

var release = githubTrivyReleaseApi.releaseByTag(trivyTag);
var resolveBinaryName = resolveBinaryName(trivyTag);

var donwloadURl = release.getAssets().stream()
.filter(asset -> asset.getName().equals(resolveBinaryName))
.findFirst();

LOG.info("Start download trivy binary from github. binary_name: ".concat(resolveBinaryName));

var pathFile = downloadBinaryFromGithubAssets(donwloadURl.get().getBrowserDownloadUrl(), resolveBinaryName);
LOG.info("Download finished");

LOG.info("Start decompress tar.gz");
var binFile = decompressTarGz(pathFile);
LOG.info("Decompress finished");

var fileBin = new File(binFile.toAbsolutePath().toString());
fileBin.setExecutable(true);

return fileBin;
}

private File getBinFileIfAlreadyExists() {
var targetPath = Paths.get("").toAbsolutePath() + "/target/trivy";
var binFile = new File(targetPath);
if (binFile.exists()) {
return binFile;
}
throw new Exception("Trivy bin not found");
return null;
}

public File extractExecutableFromJar(String executable) throws IOException {
Expand All @@ -36,9 +140,9 @@ public File extractExecutableFromJar(String executable) throws IOException {
return command;
}

public Integer scanImage(String dockerImageName, String trivyParams) throws Exception {
public Integer scanImage(String dockerImageName, String trivyParams, String trivyTag) throws Exception {
var command = "%s image --exit-code 1 %s %s";
var bin = extractExecutableFromJar(getLocationTrivyBin());
var bin = getLocationTrivyBin(trivyTag);
var finalCommand = String.format(command, bin.getAbsolutePath(), trivyParams, dockerImageName);
return execProcess(finalCommand, true);
}
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/br/com/orla/TrivyScanMojo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package br.com.orla;

import br.com.orla.api.GithubTrivyRelease;
import java.util.ArrayList;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
Expand All @@ -25,17 +26,20 @@ public class TrivyScanMojo extends AbstractMojo {
@Parameter(required = false, defaultValue = "false")
private Boolean ignoreUnfixed;

@Parameter(required = false, defaultValue = "v0.49.1")
private String trivyVersion;

@Override
public void execute() throws MojoExecutionException {
var dockerProcess = new DockerProcess();
if (dockerProcess.isDockerInstalled()) {
var defLocationDockerFile = project.getBasedir().getAbsolutePath().concat("/Dockerfile");
dockerProcess.buildDockerImage(
dockerFilePath != null ? dockerFilePath : defLocationDockerFile, project.getArtifactId());
var trivyProcess = new TrivyProcess();
var trivyProcess = new TrivyProcess(new GithubTrivyRelease());
try {
var params = buildTrivyParams();
var exitCode = trivyProcess.scanImage("app/".concat(project.getArtifactId()), params);
var exitCode = trivyProcess.scanImage("app/".concat(project.getArtifactId()), params, trivyVersion);
if (exitCode == 1) {
throw new MojoExecutionException("your app have some vulnerabilities");
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/br/com/orla/api/Assets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package br.com.orla.api;

import com.alibaba.fastjson.annotation.JSONField;

public class Assets {

private String name;

@JSONField(alternateNames = "browser_download_url")
private String browserDownloadUrl;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getBrowserDownloadUrl() {
return browserDownloadUrl;
}

public void setBrowserDownloadUrl(String browserDownloadUrl) {
this.browserDownloadUrl = browserDownloadUrl;
}
}
27 changes: 27 additions & 0 deletions src/main/java/br/com/orla/api/GithubTrivyRelease.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package br.com.orla.api;

import com.alibaba.fastjson2.JSON;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class GithubTrivyRelease implements GithubTrivyReleaseApi {

@Override
public Release releaseByTag(String tag) {
try {
var httpClient = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(10))
.uri(URI.create("https://api.github.com/repos/aquasecurity/trivy/releases/tags/".concat(tag)))
.build();
var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return JSON.parseObject(response.body(), Release.class);
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
6 changes: 6 additions & 0 deletions src/main/java/br/com/orla/api/GithubTrivyReleaseApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package br.com.orla.api;

public interface GithubTrivyReleaseApi {

Release releaseByTag(String tag);
}
16 changes: 16 additions & 0 deletions src/main/java/br/com/orla/api/Release.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package br.com.orla.api;

import java.util.List;

public class Release {

private List<Assets> assets;

public List<Assets> getAssets() {
return assets;
}

public void setAssets(List<Assets> assets) {
this.assets = assets;
}
}
Binary file removed src/main/resources/trivy_MACOS_64
Binary file not shown.
Binary file removed src/main/resources/trivy_UNIX_X86_64
Binary file not shown.
Binary file removed src/main/resources/trivy_WINDOWS_X86_64.exe
Binary file not shown.
Loading

0 comments on commit 4f29650

Please sign in to comment.