Skip to content

Commit dc2b961

Browse files
committed
[JENKINS-72557] Add ability to configure NVD API Key for OWSP dependency-check library
1 parent 0ce9650 commit dc2b961

File tree

8 files changed

+124
-4
lines changed

8 files changed

+124
-4
lines changed

Diff for: README.md

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ The builder performs an analysis using one of the pre-defined Dependency-Check C
2323

2424
![builder configuration](https://raw.githubusercontent.com/jenkinsci/dependency-check-plugin/master/docs/images/builder-config.png)
2525

26+
With 9.0.0 dependency-check has moved from using the NVD data-feed to the NVD API. Users of dependency-check are highly encouraged to obtain an NVD API Key; see https://nvd.nist.gov/developers/request-an-api-key Without an NVD API Key dependency-check's updates will be extremely slow. Please see the documentation for the cli, maven, gradle, or ant integrations on how to set the NVD API key.
27+
28+
The NVD API has enforced rate limits. If you are using a single API KEY and multiple builds occur you could hit the rate limit and receive 403 errors. In a CI environment one must use a caching strategy or an external database updated with a scheduled weekly job
29+
2630
#### Publisher
2731
The publisher works independently of the tool configuration or builder and is responsible for reading dependency-check-report.xml and generating metrics, trends, findings, and optionally failing the build or putting it into a warning state based on configurable thresholds.
2832

Diff for: docs/images/builder-config.png

-65.7 KB
Loading

Diff for: pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@
113113
<groupId>io.jenkins.plugins</groupId>
114114
<artifactId>commons-lang3-api</artifactId>
115115
</dependency>
116+
<dependency>
117+
<groupId>org.jenkins-ci.plugins</groupId>
118+
<artifactId>plain-credentials</artifactId>
119+
</dependency>
116120
<dependency>
117121
<groupId>org.apache.commons</groupId>
118122
<artifactId>commons-digester3</artifactId>

Diff for: src/main/java/org/jenkinsci/plugins/DependencyCheck/DependencyCheckToolBuilder.java

+94-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import static hudson.Util.fixEmptyAndTrim;
1919
import static hudson.Util.replaceMacro;
2020
import static hudson.util.QuotedStringTokenizer.tokenize;
21+
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
2122

2223
import java.io.IOException;
2324
import java.io.Serializable;
25+
import java.util.Collections;
2426
import java.util.List;
2527

2628
import org.apache.commons.io.FileUtils;
@@ -29,28 +31,51 @@
2931
import org.jenkinsci.plugins.DependencyCheck.tools.DependencyCheckInstallation;
3032
import org.jenkinsci.plugins.DependencyCheck.tools.DependencyCheckInstaller;
3133
import org.jenkinsci.plugins.DependencyCheck.tools.Version;
34+
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
35+
import org.kohsuke.stapler.AncestorInPath;
3236
import org.kohsuke.stapler.DataBoundConstructor;
3337
import org.kohsuke.stapler.DataBoundSetter;
38+
import org.kohsuke.stapler.QueryParameter;
39+
import org.kohsuke.stapler.verb.POST;
40+
import org.springframework.security.core.Authentication;
3441

42+
import com.cloudbees.plugins.credentials.CredentialsMatcher;
43+
import com.cloudbees.plugins.credentials.CredentialsMatchers;
44+
import com.cloudbees.plugins.credentials.CredentialsProvider;
45+
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
46+
import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
47+
48+
import edu.umd.cs.findbugs.annotations.CheckForNull;
3549
import edu.umd.cs.findbugs.annotations.NonNull;
3650
import hudson.AbortException;
3751
import hudson.EnvVars;
3852
import hudson.Extension;
3953
import hudson.FilePath;
4054
import hudson.Launcher;
55+
import hudson.Util;
4156
import hudson.XmlFile;
4257
import hudson.model.AbstractProject;
4358
import hudson.model.Cause;
4459
import hudson.model.Computer;
60+
import hudson.model.Item;
61+
import hudson.model.ItemGroup;
4562
import hudson.model.Node;
63+
import hudson.model.Queue;
4664
import hudson.model.Result;
4765
import hudson.model.Run;
4866
import hudson.model.TaskListener;
67+
import hudson.model.queue.Tasks;
68+
import hudson.security.ACL;
69+
import hudson.security.AccessControlled;
70+
import hudson.security.Permission;
4971
import hudson.tasks.BuildStepDescriptor;
5072
import hudson.tasks.Builder;
5173
import hudson.tools.InstallSourceProperty;
5274
import hudson.triggers.SCMTrigger;
5375
import hudson.util.ArgumentListBuilder;
76+
import hudson.util.FormValidation;
77+
import hudson.util.ListBoxModel;
78+
import jenkins.model.Jenkins;
5479
import jenkins.tasks.SimpleBuildStep;
5580

5681
/**
@@ -65,6 +90,7 @@ public class DependencyCheckToolBuilder extends Builder implements SimpleBuildSt
6590

6691
private final String odcInstallation;
6792
private String additionalArguments;
93+
private String nvdCredentialsId;
6894
private boolean skipOnScmChange;
6995
private boolean skipOnUpstreamChange;
7096
private boolean stopBuild = false;
@@ -192,8 +218,10 @@ private DependencyCheckInstallation getDependencyCheck() {
192218
return DependencyCheckUtil.getDependencyCheck(odcInstallation);
193219
}
194220

195-
protected ArgumentListBuilder buildArgumentList(@NonNull final String odcScript, @NonNull final Run<?, ?> build,
196-
@NonNull final FilePath workspace, @NonNull final EnvVars env) {
221+
protected ArgumentListBuilder buildArgumentList(@NonNull final String odcScript,
222+
@NonNull final Run<?, ?> build,
223+
@NonNull final FilePath workspace,
224+
@NonNull final EnvVars env) throws AbortException {
197225
final ArgumentListBuilder cliArguments = new ArgumentListBuilder(odcScript);
198226
if (!StringUtils.contains(additionalArguments, "--project")) {
199227
cliArguments.add("--project", build.getFullDisplayName());
@@ -211,6 +239,13 @@ protected ArgumentListBuilder buildArgumentList(@NonNull final String odcScript,
211239
}
212240
}
213241
}
242+
if (nvdCredentialsId != null) {
243+
StringCredentials c = CredentialsProvider.findCredentialById(nvdCredentialsId, StringCredentials.class, build);
244+
if (c == null) {
245+
throw new AbortException(Messages.Builder_DescriptorImpl_invalidCredentialsId());
246+
}
247+
cliArguments.add("--nvdApiKey").addMasked(c.getSecret());
248+
}
214249
return cliArguments;
215250
}
216251

@@ -244,6 +279,15 @@ private boolean isSkip(final Run<?, ?> build, final TaskListener listener) {
244279
return skip;
245280
}
246281

282+
public String getNvdCredentialsId() {
283+
return nvdCredentialsId;
284+
}
285+
286+
@DataBoundSetter
287+
public void setNvdCredentialsId(String nvdCredentialsId) {
288+
this.nvdCredentialsId = Util.fixEmpty(nvdCredentialsId);
289+
}
290+
247291
@Extension
248292
@Symbol({"dependencyCheck", "dependencycheck"})
249293
public static class DependencyCheckToolBuilderDescriptor extends BuildStepDescriptor<Builder> {
@@ -260,6 +304,54 @@ public void purge() {
260304
FileUtils.deleteQuietly(globalConfig.getFile());
261305
}
262306

307+
@POST
308+
public FormValidation doCheckdoNvdCredentialsId(@CheckForNull @AncestorInPath Item projectOrFolder,
309+
@QueryParameter String nvdCredentialsId) {
310+
if ((projectOrFolder == null && !Jenkins.get().hasPermission(Jenkins.ADMINISTER)) ||
311+
(projectOrFolder != null && !projectOrFolder.hasPermission(Item.EXTENDED_READ) && !projectOrFolder.hasPermission(CredentialsProvider.USE_ITEM))) {
312+
return FormValidation.ok();
313+
}
314+
if (StringUtils.isBlank(nvdCredentialsId)) {
315+
return FormValidation.warning(Messages.Builder_DescriptorImpl_emptyCredentialsId());
316+
}
317+
318+
Authentication authentication = getAuthentication(projectOrFolder);
319+
CredentialsMatcher matcher = CredentialsMatchers.withId(nvdCredentialsId);
320+
if (CredentialsProvider.listCredentialsInItem(StringCredentials.class, projectOrFolder, authentication, null, matcher).isEmpty()) {
321+
return FormValidation.error(Messages.Builder_DescriptorImpl_invalidCredentialsId());
322+
}
323+
return FormValidation.ok();
324+
}
325+
326+
@POST
327+
public ListBoxModel doFillNvdCredentialsIdItems(final @CheckForNull @AncestorInPath ItemGroup<?> context,
328+
final @CheckForNull @AncestorInPath Item projectOrFolder,
329+
@QueryParameter String nvdCredentialsId) {
330+
Permission permToCheck = projectOrFolder == null ? Jenkins.ADMINISTER : Item.CONFIGURE;
331+
AccessControlled contextToCheck = projectOrFolder == null ? Jenkins.get() : projectOrFolder;
332+
333+
// If we're on the global page and we don't have administer
334+
// permission or if we're in a project or folder
335+
// and we don't have configure permission there
336+
if (!contextToCheck.hasPermission(permToCheck)) {
337+
return new StandardUsernameListBoxModel().includeCurrentValue(trimToEmpty(nvdCredentialsId));
338+
}
339+
340+
Authentication authentication = getAuthentication(projectOrFolder);
341+
CredentialsMatcher matcher = CredentialsMatchers.instanceOf(StringCredentials.class);
342+
Class<StringCredentials> type = StringCredentials.class;
343+
ItemGroup<?> credentialsContext = context == null ? Jenkins.get() : context;
344+
345+
return new StandardListBoxModel() //
346+
.includeMatchingAs(authentication, credentialsContext, type, Collections.emptyList(), matcher) //
347+
.includeEmptyValue();
348+
}
349+
350+
@NonNull
351+
protected Authentication getAuthentication(AccessControlled item) {
352+
return item instanceof Queue.Task ? Tasks.getAuthenticationOf2((Queue.Task) item) : ACL.SYSTEM2;
353+
}
354+
263355
@NonNull
264356
@Override
265357
public String getDisplayName() {

Diff for: src/main/resources/org/jenkinsci/plugins/DependencyCheck/DependencyCheckToolBuilder/config.jelly

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
-->
1616
<?jelly escape-by-default="true"?>
17-
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
17+
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:c="/lib/credentials">
1818
<f:entry title="${%installation.title}" description="${%installation.description}">
1919
<select class="setting-input" name="_.odcInstallation">
2020
<j:forEach var="inst" items="${descriptor.installations}">
@@ -23,6 +23,10 @@ limitations under the License.
2323
</select>
2424
</f:entry>
2525

26+
<f:entry title="${%nvdCredentialsId.title}" field="nvdCredentialsId">
27+
<c:select />
28+
</f:entry>
29+
2630
<f:entry title="${%arguments}" field="additionalArguments">
2731
<f:textarea/>
2832
</f:entry>

Diff for: src/main/resources/org/jenkinsci/plugins/DependencyCheck/DependencyCheckToolBuilder/config.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ installation.notspecified=Please define a Dependency-Check installation in the J
1919
arguments=Arguments
2020
scm.skip=Skip if triggered by SCM changes
2121
upstream.skip=Skip if triggered by upstream changes
22-
stopBuild.title=Stop current build when scanner return exception
22+
stopBuild.title=Stop current build when scanner return exception
23+
nvdCredentialsId.title=NVD API Key (version 9 or later)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<div>
2+
<p>
3+
With 9.0.0 dependency-check has moved from using the NVD data-feed to the NVD API.<br>
4+
Users of dependency-check are <b>highly</b> encouraged to obtain an NVD API Key;
5+
see https://nvd.nist.gov/developers/request-an-api-key<br>
6+
Without an NVD API Key dependency-check's updates will be <b>extremely slow</b>.
7+
</p>
8+
<p>
9+
<b>The NVD API Key, CI, and Rate Limiting</b><br>
10+
The NVD API has enforced rate limits. If you are using a single API KEY and multiple builds occur you could hit the rate limit and receive 403 errors.<br>
11+
<u>In a CI environment one must use a caching strategy or use a set API KEY to use for different jobs.</u>
12+
</p>
13+
</div>

Diff for: src/main/resources/org/jenkinsci/plugins/DependencyCheck/Messages.properties

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Builder.noExecutableFound=Couldn\u2019t find any executable in "{0}"
2828
Builder.Skip=Skipping Dependency-Check analysis
2929
Builder.noInstallationFound=No installation {0} found. Please define one in manager Jenkins.
3030
Builder.nodeOffline=Cannot get installation for node, since it is not online
31+
Builder.DescriptorImpl.emptyCredentialsId=Credentials is required
32+
Builder.DescriptorImpl.invalidCredentialsId=Current credentials does not exists
3133

3234
Platform.unknown=Unknown OS name: {0}
3335
SystemTools.nodeNotAvailable=Node could be offline or there are no executor defined for Node {0}

0 commit comments

Comments
 (0)