VYPR
High severityNVD Advisory· Published Mar 30, 2021· Updated Nov 19, 2024

CVE-2021-21633

CVE-2021-21633

Description

CSRF in Jenkins OWASP Dependency-Track Plugin ≤3.1.0 lets attackers connect to attacker-controlled URLs, capturing stored Jenkins credentials.

AI Insight

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

CSRF in Jenkins OWASP Dependency-Track Plugin ≤3.1.0 lets attackers connect to attacker-controlled URLs, capturing stored Jenkins credentials.

Vulnerability

Overview CVE-2021-21633 is a cross-site request forgery (CSRF) vulnerability in the Jenkins OWASP Dependency-Track Plugin version 3.1.0 and earlier [1][3]. The plugin fails to require a CSRF token for certain HTTP endpoints, allowing an attacker to trick an authenticated Jenkins user into making a request that causes the plugin to connect to an attacker-specified URL [1]. This action can capture credentials stored in Jenkins, such as API keys or other secrets.

Exploitation

An attacker can exploit this vulnerability by crafting a malicious link or form and luring a Jenkins user with sufficient permissions (e.g., Job/Configure or overall Configure access) to click it [1]. The plugin then sends a request to the attacker-controlled URL, transmitting stored credentials in the process [3]. No direct network access to Jenkins is required; the attack relies on social engineering.

Impact

Successful exploitation allows the attacker to obtain credentials stored in Jenkins, which could be used to access other systems or escalate privileges within Jenkins [1][3]. The severity is rated as medium (CVSS 6.5) according to the Jenkins security advisory [1].

Mitigation

The vulnerability is fixed in OWASP Dependency-Track Plugin version 3.1.1 [4]. Users should upgrade immediately. No workaround is available [1].

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