Skip to content

Commit

Permalink
Feature/project integration updates (#429)
Browse files Browse the repository at this point in the history
* Implement isLatest for Dependency BOM Upload API

* Use lookup API-Call

* Add the possibilty for project tags

* Added missing docs for new features

* Add new features to test profile

* Bump up to java 21

* Downgrade to java 17

* Update github action to java 17

* Update github sonar action to java 17

* Update github sonar action to use feature branch regex

---------

Co-authored-by: Philipp Dallig <[email protected]>
Co-authored-by: Paul McKeown <[email protected]>
  • Loading branch information
3 people authored Nov 17, 2024
1 parent 1352bee commit 152ea76
Show file tree
Hide file tree
Showing 36 changed files with 688 additions and 414 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches: [ feature/* ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Analyze with SonarCloud
Expand Down
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,40 @@ Dependency-Track, so this has no default to allow for blank values.
**Note:** If the parent cannot be found on the Dependency-Track server, the BOM upload will not be attempted in order to
prevent a project being incorrectly created or updated the server.

| Property | Required | Default Value | Example Values |
|-------------------|----------|------------------------|---------------------------|
| bomLocation | false | target/bom.xml | target/custom-bom.xml |
| updateProjectInfo | false | false | false |
| updateParent | false | false | true |
| parentName | false | ${project.parent.name} | my-name-override |
| parentVersion | false | | ${project.parent.version} |
| Property | Required | Default Value | Example Values |
|----------------------|----------|------------------------|---------------------------|
| bomLocation | false | target/bom.xml | target/custom-bom.xml |
| updateProjectInfo | false | false | false |
| updateParent | false | false | true |
| parentName | false | ${project.parent.name} | my-name-override |
| parentVersion | false | | ${project.parent.version} |
| isLatest | false | false | true |
| projectTags[].name | false | false | <name>tag1</name> |

The `isLatest` option sets the flag on the project to indicate that it is the latest version.

The `projectTags` option allows for tags to be added to a project. This adds project tags only, and doesn't reconcile
the tags on the remote server, so if they are removed from the list or modified, they will need to be removed or
modified on the server to reflect the new state.

Example:

```xml
<execution>
<id>upload-bom</id>
<phase>verify</phase>
<goals>
<goal>upload-bom</goal>
</goals>
<configuration>
<updateProjectInfo>true</updateProjectInfo>
<isLatest>true</isLatest>
<projectTags>
<name>tag1</name>
</projectTags>
</configuration>
</execution>
```

### Get Project Findings
After a BOM upload, the best way to determine if there are any vulnerabilities is to use the `findings` goal which is
Expand Down
18 changes: 11 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>io.github.pmckeown</groupId>
<artifactId>dependency-track-maven-plugin</artifactId>
<version>1.7.1</version>
<version>1.7.2-SNAPSHOT</version>
<packaging>maven-plugin</packaging>

<name>Dependency Track Maven Plugin</name>
Expand Down Expand Up @@ -65,8 +65,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.version>3.9.5</maven.version>
</properties>

Expand All @@ -80,17 +80,17 @@
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-objectmapper-jackson</artifactId>
<version>3.14.2</version>
<version>3.14.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.2</version>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down Expand Up @@ -163,7 +163,7 @@
<dependency>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-core-java</artifactId>
<version>7.3.2</version>
<version>9.0.4</version>
</dependency>

<!-- Test dependencies -->
Expand Down Expand Up @@ -507,6 +507,10 @@
</goals>
<configuration>
<updateProjectInfo>true</updateProjectInfo>
<isLatest>true</isLatest>
<projectTags>
<name>dog-food</name>
</projectTags>
</configuration>
</execution>
<execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public final class ResourceConstants {

public static final String V1_BOM = "/api/v1/bom";
public static final String V1_BOM_TOKEN_UUID = "/api/v1/bom/token/{uuid}";
public static final String V1_PROJECT = "/api/v1/project?limit=1000000&offset=0";
public static final String V1_PROJECT_LOOKUP = "/api/v1/project/lookup";
public static final String V1_PROJECT_UUID = "/api/v1/project/{uuid}";
public static final String V1_FINDING_PROJECT_UUID = "/api/v1/finding/project/{uuid}";
public static final String V1_METRICS_PROJECT_UUID_CURRENT = "/api/v1/metrics/project/{uuid}/current";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import io.github.pmckeown.util.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.cyclonedx.BomParserFactory;

import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
import org.cyclonedx.parsers.BomParserFactory;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.FileInputStream;
import java.util.Optional;

/**
Expand Down Expand Up @@ -41,11 +41,11 @@ public BomParser(Logger logger) {
*/
public Optional<ProjectInfo> getProjectInfo(File bomFile) {
if (!bomFile.canRead()) {
logger.warn("Can not read bom {}", bomFile);
return Optional.empty();
}
Bom bom;
try {
BOMInputStream bis = new BOMInputStream(new FileInputStream(bomFile), false);
try (BOMInputStream bis = BOMInputStream.builder().setFile(bomFile).setInclude(false).get()) {
byte[] bytes = IOUtils.toByteArray(bis);
bom = BomParserFactory.createParser(bytes).parse(bytes);
} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.github.pmckeown.dependencytrack.Item;
import io.github.pmckeown.dependencytrack.metrics.Metrics;

import java.util.List;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

Expand All @@ -16,14 +19,22 @@ public class Project extends Item {
private String name;
private String version;
private Metrics metrics;
private boolean isLatest;
private List<ProjectTag> tags;

@JsonCreator
public Project(@JsonProperty("uuid") String uuid, @JsonProperty("name") String name,
@JsonProperty("version") String version, @JsonProperty("metrics") Metrics metrics) {
public Project(@JsonProperty("uuid") String uuid,
@JsonProperty("name") String name,
@JsonProperty("version") String version,
@JsonProperty("metrics") Metrics metrics,
@JsonProperty("isLatest") boolean isLatest,
@JsonProperty("tags") List<ProjectTag> tags) {
super(uuid);
this.name = name;
this.version = version;
this.metrics = metrics;
this.isLatest = isLatest;
this.tags = tags;
}

public String getName() {
Expand All @@ -38,6 +49,14 @@ public Metrics getMetrics() {
return metrics;
}

public boolean isLatest() {
return isLatest;
}

public List<ProjectTag> getTags() {
return tags;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import io.github.pmckeown.dependencytrack.bom.BomParser;
import io.github.pmckeown.util.Logger;
import kong.unirest.UnirestException;
import org.apache.commons.lang3.StringUtils;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static java.lang.String.format;

Expand All @@ -33,21 +36,15 @@ public ProjectAction(ProjectClient projectClient, BomParser bomParser, Logger lo

public Project getProject(String projectName, String projectVersion) throws DependencyTrackException {
try {
Response<List<Project>> response = projectClient.getProjects();
Response<Project> response = projectClient.getProject(projectName, projectVersion);

if (response.isSuccess()) {
Optional<List<Project>> body = response.getBody();
Optional<Project> body = response.getBody();
if (body.isPresent()) {
Optional<Project> project = findProject(body.get(), projectName, projectVersion);

if (project.isPresent()) {
return project.get();
} else {
throw new DependencyTrackException(
format("Requested project not found: %s-%s", projectName, projectVersion));
}
return body.get();
} else {
throw new DependencyTrackException("No projects found on server.");
throw new DependencyTrackException(
format("Requested project not found: %s-%s", projectName, projectVersion));
}
} else {
logger.error("Failed to list projects with error from server: " + response.getStatusText());
Expand All @@ -59,17 +56,33 @@ public Project getProject(String projectName, String projectVersion) throws Depe
}

public boolean updateProject(Project project, UpdateRequest updateReq) throws DependencyTrackException {
return updateProject(project, updateReq, Collections.emptySet());
}

public boolean updateProject(Project project, UpdateRequest updateReq, Set<String> projectTags) throws DependencyTrackException {
ProjectInfo info = null;
if (updateReq.hasBomLocation()) {
logger.info("Project info will be updated");
Optional<ProjectInfo> optInfo = bomParser.getProjectInfo(new File(updateReq.getBomLocation()));
if (optInfo.isPresent()) {
info = optInfo.get();
info.setIsLatest(Boolean.valueOf(project.isLatest()));
} else {
logger.warn("Could not create ProjectInfo from bom at location: %s", updateReq.getBomLocation());
return false;
}
}
if (projectTags != null && !projectTags.isEmpty()) {
if (info == null) {
info = new ProjectInfo();
}
if (project.getTags() != null && !project.getTags().isEmpty()) {
logger.info("Merging Project Tags");
info.setTags(mergeTags(project.getTags(), projectTags));
} else {
info.setTags(projectTags.stream().map(ProjectTag::new).collect(Collectors.toList()));
}
}

if (updateReq.hasParent()) {
logger.info("Project parent will be updated");
Expand Down Expand Up @@ -115,10 +128,19 @@ boolean deleteProject(Project project) throws DependencyTrackException {
}
}

private Optional<Project> findProject(List<Project> projects, String projectName, String projectVersion) {
// The project version may be null from the Dependency-Track server
return projects.stream()
.filter(project -> projectName.equals(project.getName()) && StringUtils.equals(projectVersion, project.getVersion()))
.findFirst();
private List<ProjectTag> mergeTags(List<ProjectTag> existingTags, Set<String> mavenTags) {
List<ProjectTag> projectTags = new LinkedList<>(existingTags);
for (String mavenTag : mavenTags) {
boolean exists = false;
for (ProjectTag projectTag : projectTags) {
if (projectTag.getName().equals(mavenTag)) {
exists = true;
}
}
if (!exists) {
projectTags.add(new ProjectTag(mavenTag));
}
}
return projectTags;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Optional;

import static io.github.pmckeown.dependencytrack.ResourceConstants.V1_PROJECT;
import static io.github.pmckeown.dependencytrack.ResourceConstants.V1_PROJECT_UUID;
import static io.github.pmckeown.dependencytrack.ResourceConstants.V1_PROJECT_LOOKUP;
import static kong.unirest.Unirest.*;

/**
Expand All @@ -32,18 +31,19 @@ public ProjectClient(CommonConfig commonConfig) {
this.commonConfig = commonConfig;
}

public Response<List<Project>> getProjects() {
HttpResponse<List<Project>> httpResponse = get(commonConfig.getDependencyTrackBaseUrl() + V1_PROJECT)
.header(X_API_KEY, commonConfig.getApiKey())
.asObject(new GenericType<List<Project>>(){});

Optional<List<Project>> body;
public Response<Project> getProject(String projectName, String projectVersion) {
HttpResponse<Project> httpResponse = get(commonConfig.getDependencyTrackBaseUrl() + V1_PROJECT_LOOKUP)
.queryString("name", projectName)
.queryString("version", projectVersion)
.header(X_API_KEY, commonConfig.getApiKey())
.asObject(new GenericType<Project>() {
});
Optional<Project> body;
if (httpResponse.isSuccess()) {
body = Optional.of(httpResponse.getBody());
} else {
body = Optional.empty();
}

return new Response<>(httpResponse.getStatus(), httpResponse.getStatusText(), httpResponse.isSuccess(), body);
}

Expand Down
Loading

0 comments on commit 152ea76

Please sign in to comment.