Skip to content

Commit

Permalink
add Link to DT Project page on the sidebar
Browse files Browse the repository at this point in the history
fixes #JENKINS-55627
  • Loading branch information
sephiroth-j committed Nov 8, 2020
1 parent 56ad0de commit 61b0ff7
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 15 deletions.
Expand Up @@ -157,6 +157,12 @@ public void perform(@NonNull Run<?, ?> run, @NonNull FilePath workspace, @NonNul
throw new AbortException(Messages.Builder_Upload_Failed());
}

// add ResultLinkAction event it may not contain a projectId. but we want to store name version for the future.
final ResultLinkAction linkAction = new ResultLinkAction(getEffectiveUrl(), projectId);
linkAction.setProjectName(projectName);
linkAction.setProjectVersion(projectVersion);
run.addOrReplaceAction(linkAction);

logger.log(Messages.Builder_Success(String.format("%s/projects/%s", getEffectiveUrl(), projectId != null ? projectId : StringUtils.EMPTY)));

if (synchronous && StringUtils.isNotBlank(uploadResult.getToken())) {
Expand Down Expand Up @@ -187,7 +193,15 @@ private void publishAnalysisResult(ConsoleLogger logger, final ApiClient apiClie
final SeverityDistribution severityDistribution = new SeverityDistribution(build.getNumber());
findings.stream().map(Finding::getVulnerability).map(Vulnerability::getSeverity).forEach(severityDistribution::add);
final ResultAction projectAction = new ResultAction(findings, severityDistribution);
projectAction.setDependencyTrackUrl(getEffectiveUrl());
projectAction.setProjectId(projectId);
build.addOrReplaceAction(projectAction);

// update ResultLinkAction with one that surely contains a projectId
final ResultLinkAction linkAction = new ResultLinkAction(getEffectiveUrl(), projectId);
linkAction.setProjectName(projectName);
linkAction.setProjectVersion(projectVersion);
build.addOrReplaceAction(linkAction);

// Get previous results and evaluate to thresholds
final SeverityDistribution previousSeverityDistribution = Optional.ofNullable(build.getPreviousBuild())
Expand Down
Expand Up @@ -26,6 +26,7 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.sf.json.JSONArray;
import org.jenkinsci.plugins.DependencyTrack.model.Finding;
import org.jenkinsci.plugins.DependencyTrack.model.SeverityDistribution;
Expand All @@ -35,21 +36,34 @@
@EqualsAndHashCode
@RequiredArgsConstructor
public class ResultAction implements RunAction2, SimpleBuildStep.LastBuildAction, Serializable {

private static final long serialVersionUID = 9144544646132489130L;

private transient Run<?, ?> run; // transient: see RunAction2, and JENKINS-45892
private final List<Finding> findings;
private final SeverityDistribution severityDistribution;

/**
* the URL of the Dependency-Track Server to which these results are
* belonging to
*/
@Setter
private String dependencyTrackUrl;

/**
* the ID of the project to which these results are belonging to
*/
@Setter
private String projectId;

@Override
public String getIconFileName() {
return "/plugin/dependency-track/icons/dt-logo-symbol.svg";
}

@Override
public String getDisplayName() {
return "Dependency-Track Report";
return Messages.Result_DT_Report();
}

@Override
Expand Down
@@ -0,0 +1,91 @@
/*
* Copyright 2020 OWASP.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.DependencyTrack;

import hudson.model.Run;
import java.io.Serializable;
import jenkins.model.RunAction2;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;

/**
* Action to Provide a Link to the Project on the Dependency-Track Server
*
* @author Ronny "Sephiroth" Perinke <sephiroth@sephiroth-j.de>
*/
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor
public class ResultLinkAction implements RunAction2, Serializable {

private static final long serialVersionUID = 9144463546984654654L;

/**
* the URL of the Dependency-Track Server to which these results are
* belonging to
*/
private final String dependencyTrackUrl;

/**
* the ID of the project to which these results are belonging to
*/
private final String projectId;

/**
* the name of the project to which these results are belonging to
*/
@Setter
private String projectName;

/**
* the version of the project to which these results are belonging to
*/
@Setter
private String projectVersion;

@Override
public String getIconFileName() {
return isEnabled() ? "/plugin/dependency-track/icons/dt-logo-symbol.svg" : null;
}

@Override
public String getDisplayName() {
return isEnabled() ? Messages.Result_DT_Project(): null;
}

@Override
public String getUrlName() {
return isEnabled() ? String.format("%s/projects/%s", dependencyTrackUrl, projectId) : null;
}

@Override
public void onAttached(Run<?, ?> run) {
// nothing to do
}

@Override
public void onLoad(Run<?, ?> run) {
// nothing to do
}

private boolean isEnabled() {
return StringUtils.isNotBlank(dependencyTrackUrl) && StringUtils.isNotBlank(projectId);
}

}
Expand Up @@ -35,3 +35,6 @@ ApiClient.Error.Connection=An error occurred connecting to Dependency-Track - HT
ApiClient.Error.TokenProcessing=An error occurred while checking if a token is being processed - HTTP response code: {0} {1}
ApiClient.Error.RetrieveFindings=An error occurred while retrieving findings - HTTP response code: {0} {1}
ApiClient.Error.ProjectLookup=An error occurred while looking up project id for name "{0}" and version "{1}" - HTTP response code: {2} {3}

Result.DT.Report=Dependency-Track Report
Result.DT.Project=Dependency-Track Project
Expand Up @@ -23,7 +23,12 @@
</script>
<script type="text/javascript" src="${resURL}/plugin/dependency-track/js/result-action.js" defer="defer"></script>

<h1>${it.displayName} for ${it.run.displayName}</h1>
<h1>${it.displayName} for ${it.run.parent.displayName}${it.run.displayName}</h1>
<j:if test="${it.dependencyTrackUrl != null and !it.dependencyTrackUrl.isEmpty()}">
<div class="alert alert-warning" role="alert">
These results may not be up to date. Please visit <a class="alert-link" href="${it.dependencyTrackUrl}/projects/${it.projectId}">the Dependency Track project page</a> for the most recent results.
</div>
</j:if>

<div id="app">
<b-form-group
Expand Down Expand Up @@ -90,12 +95,12 @@
<template slot="cell(component.name)" slot-scope="data">
<b-icon :icon="data.detailsShowing ? 'dash-square' : 'plus-square'" v-on:click="data.toggleDetails"></b-icon> {{data.value}}
</template>
<template slot="cell(vulnerability.vulnId)" slot-scope="data">
<template slot="cell(vulnerability.vulnId)" slot-scope="data"><j:whitespace>
<b-badge pill="pill" variant="info">{{data.item.vulnerability.source}}</b-badge>
<a v-if="data.item.vulnerability.source === 'NVD'" :href="'https://nvd.nist.gov/vuln/detail/'+data.value" title="View Details on NVD" class="vulnid" rel="noopener noreferrer" referrerpolicy="no-referrer">{{data.value}}</a>
<a v-else-if="data.item.vulnerability.source === 'OSSINDEX'" :href="'https://ossindex.sonatype.org/vuln/'+data.value" title="View Details on Sonatype OSS INDEX" class="vulnid" rel="noopener noreferrer" referrerpolicy="no-referrer">{{data.value}}</a>
<a v-if="data.item.vulnerability.source === 'NVD'" :href="'https://nvd.nist.gov/vuln/detail/'+data.value" title="View Details on NVD" class="font-weight-normal" rel="noopener noreferrer" referrerpolicy="no-referrer">{{data.value}}</a>
<a v-else-if="data.item.vulnerability.source === 'OSSINDEX'" :href="'https://ossindex.sonatype.org/vuln/'+data.value" title="View Details on Sonatype OSS INDEX" class="font-weight-normal" rel="noopener noreferrer" referrerpolicy="no-referrer">{{data.value}}</a>
<template v-else="v-else">{{data.value}}</template>
</template>
</j:whitespace></template>
<template slot="cell(vulnerability.severityRank)" slot-scope="data">
<b-badge variant="danger" v-if="data.value === 0"><b-icon icon="bug-fill" font-scale="1.5"></b-icon></b-badge>
<b-badge variant="warning" v-if="data.value === 1"><b-icon icon="bug-fill" font-scale="1.5"></b-icon></b-badge>
Expand All @@ -106,7 +111,7 @@
{{data.item.vulnerability.severity}}
</template>
<template slot="cell(vulnerability.cweId)" slot-scope="data" v-if="data.value">
<a :href="'https://cwe.mitre.org/data/definitions/'+data.value+'.html'" title="View Details on MITRE" class="cweid" rel="noopener noreferrer" referrerpolicy="no-referrer">CWE-{{data.value}} {{data.item.vulnerability.cweName}}</a>
<a :href="'https://cwe.mitre.org/data/definitions/'+data.value+'.html'" title="View Details on MITRE" class="font-weight-normal" rel="noopener noreferrer" referrerpolicy="no-referrer">CWE-{{data.value}} {{data.item.vulnerability.cweName}}</a>
</template>
<template v-slot:row-details="row">
<b-card
Expand Down
7 changes: 0 additions & 7 deletions src/main/webapp/css/result-action.css
@@ -1,10 +1,3 @@
a.vulnid, a.cweid {
font-weight: normal;
}
#app .badge+a.vulnid {
margin-left: .5ch;
}

/*
Support for dark Theme of Jenkins in combination with bootstrap
*/
Expand Down
@@ -0,0 +1,60 @@
/*
* Copyright 2020 OWASP.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jenkinsci.plugins.DependencyTrack;

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
*
* @author Ronny "Sephiroth" Perinke <sephiroth@sephiroth-j.de>
*/
public class ResultLinkActionTest {

@Test
public void testWithMissingUrlAndProjectId() {
ResultLinkAction uut = new ResultLinkAction(null, null);
assertThat(uut.getUrlName()).isNull();
assertThat(uut.getDisplayName()).isNull();
assertThat(uut.getIconFileName()).isNull();
}

@Test
public void testWithMissingUrl() {
ResultLinkAction uut = new ResultLinkAction(null, "an-id");
assertThat(uut.getUrlName()).isNull();
assertThat(uut.getDisplayName()).isNull();
assertThat(uut.getIconFileName()).isNull();
}

@Test
public void testWithMissingProjectId() {
ResultLinkAction uut = new ResultLinkAction("http://foo.bar", null);
assertThat(uut.getUrlName()).isNull();
assertThat(uut.getDisplayName()).isNull();
assertThat(uut.getIconFileName()).isNull();
}

@Test
public void testWithUrlAndProjectId() {
ResultLinkAction uut = new ResultLinkAction("http://foo.bar", "an-id");
assertThat(uut.getUrlName()).isEqualTo("http://foo.bar/projects/an-id");
assertThat(uut.getDisplayName()).isNotEmpty();
assertThat(uut.getIconFileName()).isNotEmpty();
}

}

0 comments on commit 61b0ff7

Please sign in to comment.