VYPR
Moderate severityNVD Advisory· Published Mar 30, 2021· Updated Aug 3, 2024

CVE-2021-21632

CVE-2021-21632

Description

A missing permission check in Jenkins OWASP Dependency-Track Plugin 3.1.0 and earlier lets attackers with Overall/Read capture Jenkins credentials via attacker-specified URLs.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A missing permission check in Jenkins OWASP Dependency-Track Plugin 3.1.0 and earlier lets attackers with Overall/Read capture Jenkins credentials via attacker-specified URLs.

Vulnerability

Description

The Jenkins OWASP Dependency-Track Plugin, versions 3.1.0 and earlier, contains a missing permission check that allows attackers with Overall/Read permission to connect to an attacker-specified URL [1][3]. This vulnerability arises from insufficient authorization enforcement when the plugin performs network requests to external services, enabling credential capture from Jenkins.

Exploitation

An attacker can exploit this issue by leveraging the Overall/Read permission (a low-level default permission for many users) to configure the plugin to connect to a malicious URL they control [1]. Since the plugin is designed to interact with Dependency-Track servers, the missing check allows directing those connections to an attacker-chosen endpoint without proper authentication validation.

Impact

Successful exploitation reveals credentials stored in Jenkins, such as API keys or other secrets used in the OWASP Dependency-Track Plugin configuration [1][3]. The captured credentials could then be reused to access other systems or escalate privileges within Jenkins environments.

Mitigation

Jenkins released OWASP Dependency-Track Plugin version 3.1.1 to fix the missing permission check [4]. All users should upgrade to this version or later. No workaround is mentioned, and the vulnerability is not known to be listed in CISA KEV.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:dependency-trackMaven
< 3.1.13.1.1

Affected products

2

Patches

1
70e7b82ad9a1

[SECURITY-2250]

5 files changed · +37 11
  • CHANGELOG.md+3 0 modified
    @@ -1,6 +1,9 @@
     # Dependency-Track Jenkins Plugin - Changelog
     
     ## Unreleased
    +## v3.1.1 - 2021-03-30
    +### 🐞 Bugs Fixed
    +- [SECURITY-2250](https://issues.jenkins.io/browse/SECURITY-2250). Thanks to Justin Philip for reporting this issue.
     
     ## v3.1.0 - 2021-02-08
     ### ⭐ New Features
    
  • src/main/java/org/jenkinsci/plugins/DependencyTrack/DescriptorImpl.java+26 3 modified
    @@ -46,6 +46,7 @@
     import org.kohsuke.stapler.DataBoundSetter;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
    +import org.kohsuke.stapler.verb.POST;
     
     /**
      * <p>
    @@ -149,6 +150,7 @@ public boolean isApplicable(Class<? extends AbstractProject> aClass) {
          * @param item used to lookup credentials in job config. ignored in global
          * @return ListBoxModel
          */
    +    @POST
         public ListBoxModel doFillProjectIdItems(@QueryParameter final String dependencyTrackUrl, @QueryParameter final String dependencyTrackApiKey, @AncestorInPath @Nullable Item item) {
             final ListBoxModel projects = new ListBoxModel();
             try {
    @@ -169,6 +171,7 @@ public ListBoxModel doFillProjectIdItems(@QueryParameter final String dependency
             return projects;
         }
     
    +    @POST
         public ListBoxModel doFillDependencyTrackApiKeyItems(@QueryParameter String credentialsId, @AncestorInPath Item item) {
             StandardListBoxModel result = new StandardListBoxModel();
             if (item == null) {
    @@ -190,19 +193,33 @@ public ListBoxModel doFillDependencyTrackApiKeyItems(@QueryParameter String cred
          * Performs input validation when submitting the global config
          *
          * @param value The value of the URL as specified in the global config
    +     * @param item used to check permissions in job config. ignored in global
          * @return a FormValidation object
          */
    -    public FormValidation doCheckDependencyTrackUrl(@QueryParameter String value) {
    +    @POST
    +    public FormValidation doCheckDependencyTrackUrl(@QueryParameter String value, @AncestorInPath @Nullable Item item) {
    +        if (item == null) {
    +            Jenkins.get().checkPermission(Jenkins.ADMINISTER);
    +        } else {
    +            item.checkPermission(Item.CONFIGURE);
    +        }
             return PluginUtil.doCheckUrl(value);
         }
     
         /**
          * Performs input validation when submitting the global config
          *
          * @param value The value of the URL as specified in the global config
    +     * @param item used to check permissions in job config. ignored in global
          * @return a FormValidation object
          */
    -    public FormValidation doCheckDependencyTrackFrontendUrl(@QueryParameter String value) {
    +    @POST
    +    public FormValidation doCheckDependencyTrackFrontendUrl(@QueryParameter String value, @AncestorInPath @Nullable Item item) {
    +        if (item == null) {
    +            Jenkins.get().checkPermission(Jenkins.ADMINISTER);
    +        } else {
    +            item.checkPermission(Item.CONFIGURE);
    +        }
             return PluginUtil.doCheckUrl(value);
         }
     
    @@ -217,12 +234,18 @@ public FormValidation doCheckDependencyTrackFrontendUrl(@QueryParameter String v
          * config
          * @return FormValidation
          */
    +    @POST
         public FormValidation doTestConnection(@QueryParameter final String dependencyTrackUrl, @QueryParameter final String dependencyTrackApiKey, @AncestorInPath @Nullable Item item) {
    +        if (item == null) {
    +            Jenkins.get().checkPermission(Jenkins.ADMINISTER);
    +        } else {
    +            item.checkPermission(Item.CONFIGURE);
    +        }
             // url may come from instance-config. if empty, then take it from global config (this)
             final String url = Optional.ofNullable(PluginUtil.parseBaseUrl(dependencyTrackUrl)).orElse(getDependencyTrackUrl());
             // api-key may come from instance-config. if empty, then take it from global config (this)
             final String apiKey = lookupApiKey(Optional.ofNullable(StringUtils.trimToNull(dependencyTrackApiKey)).orElse(getDependencyTrackApiKey()), item);
    -        if (doCheckDependencyTrackUrl(url).kind == FormValidation.Kind.OK && StringUtils.isNotBlank(apiKey)) {
    +        if (doCheckDependencyTrackUrl(url, item).kind == FormValidation.Kind.OK && StringUtils.isNotBlank(apiKey)) {
                 try {
                     final ApiClient apiClient = getClient(url, apiKey);
                     final String result = apiClient.testConnection();
    
  • src/main/resources/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher/config.jelly+2 2 modified
    @@ -39,10 +39,10 @@ limitations under the License.
     
         <f:optionalBlock inline="true" field="overrideGlobals" title="${%overrideGlobals}">
         <f:entry title="${%dependencytrack.url}" field="dependencyTrackUrl" help="/plugin/dependency-track/help-dt-url.html">
    -        <f:textbox id="dependencytrack.url" />
    +        <f:textbox id="dependencytrack.url" checkMethod="post" />
         </f:entry>
         <f:entry title="${%dependencytrack.url.frontend}" field="dependencyTrackFrontendUrl" help="/plugin/dependency-track/help-dt-url-frontend.html">
    -        <f:textbox id="dependencytrack.url.frontend"/>
    +        <f:textbox id="dependencytrack.url.frontend" checkMethod="post" />
         </f:entry>
         <f:entry title="${%dependencytrack.apikey}" field="dependencyTrackApiKey" help="/plugin/dependency-track/help-dt-apikey.html">
             <c:select id="dependencytrack.apikey" />
    
  • src/main/resources/org/jenkinsci/plugins/DependencyTrack/DependencyTrackPublisher/global.jelly+2 2 modified
    @@ -18,7 +18,7 @@ limitations under the License.
     
         <f:section title="Dependency-Track">
             <f:entry title="${%dependencytrack.url}" field="dependencyTrackUrl" help="/plugin/dependency-track/help-dt-url.html">
    -            <f:textbox id="dependencytrack.url"/>
    +            <f:textbox id="dependencytrack.url" checkMethod="post" />
             </f:entry>
             <f:entry title="${%dependencytrack.apikey}" field="dependencyTrackApiKey" help="/plugin/dependency-track/help-dt-apikey.html">
                 <c:select id="dependencytrack.apikey" />
    @@ -28,7 +28,7 @@ limitations under the License.
             </f:entry>
             <f:advanced>
                 <f:entry title="${%dependencytrack.url.frontend}" field="dependencyTrackFrontendUrl" help="/plugin/dependency-track/help-dt-url-frontend.html">
    -                <f:textbox id="dependencytrack.url.frontend"/>
    +                <f:textbox id="dependencytrack.url.frontend" checkMethod="post" />
                 </f:entry>
                 <f:entry title="${%dependencytrack.polling.timeout}" field="dependencyTrackPollingTimeout" help="/plugin/dependency-track/help-dt-polling-timeout.html">
                     <f:number id="dependencytrack.polling.timeout" default="5" clazz="positive-number" />
    
  • src/test/java/org/jenkinsci/plugins/DependencyTrack/DescriptorImplTest.java+4 4 modified
    @@ -162,10 +162,10 @@ public void doTestConnectionTestWithEmptyArgs() throws ApiClientException, IOExc
     
         @Test
         public void doCheckDependencyTrackUrlTest() {
    -        assertThat(uut.doCheckDependencyTrackUrl("http://foo.bar/")).isEqualTo(FormValidation.ok());
    -        assertThat(uut.doCheckDependencyTrackUrl("http://foo.bar")).isEqualTo(FormValidation.ok());
    -        assertThat(uut.doCheckDependencyTrackUrl("")).isEqualTo(FormValidation.ok());
    -        assertThat(uut.doCheckDependencyTrackUrl("foo"))
    +        assertThat(uut.doCheckDependencyTrackUrl("http://foo.bar/", null)).isEqualTo(FormValidation.ok());
    +        assertThat(uut.doCheckDependencyTrackUrl("http://foo.bar", null)).isEqualTo(FormValidation.ok());
    +        assertThat(uut.doCheckDependencyTrackUrl("", null)).isEqualTo(FormValidation.ok());
    +        assertThat(uut.doCheckDependencyTrackUrl("foo", null))
                     .hasFieldOrPropertyWithValue("kind", FormValidation.Kind.ERROR)
                     .hasMessage("The specified value is not a valid URL");
         }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

1