VYPR
High severityNVD Advisory· Published Mar 8, 2023· Updated Feb 28, 2025

CVE-2023-27901

CVE-2023-27901

Description

Jenkins 2.393 and earlier, LTS 2.375.3 and earlier uses the Apache Commons FileUpload library without specifying limits for the number of request parts introduced in version 1.5 for CVE-2023-24998 in org.kohsuke.stapler.RequestImpl, allowing attackers to trigger a denial of service.

AI Insight

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

Jenkins core fails to limit the number of multipart request parts, allowing unauthenticated denial of service via resource exhaustion.

Vulnerability

Description Jenkins 2.393 and earlier, as well as LTS 2.375.3 and earlier, uses the Apache Commons FileUpload library without specifying limits for the number of request parts. This missing limit, introduced in Commons FileUpload 1.5 for CVE-2023-24998, affects the org.kohsuke.stapler.RequestImpl class [2]. The flaw allows an attacker to send a crafted multipart/form-data HTTP request containing an excessive number of parts.

Exploitation

An attacker can exploit this vulnerability by sending a multipart HTTP request with a large number of parts to a Jenkins instance. No authentication is required to trigger the issue. Each part consumes server resources during parsing, and without a maximum limit, the server may allocate excessive memory and CPU, leading to degradation or complete unavailability [2][3].

Impact

Successful exploitation results in a denial of service (DoS). The Jenkins master may become unresponsive or crash, disrupting CI/CD operations. The impact is limited to availability; no data confidentiality or integrity is directly compromised.

Mitigation

The vulnerability is fixed in Jenkins 2.394, LTS 2.375.4, and LTS 2.387.1 [3]. The fix adds a configurable file count limit (default 1000) via the system property org.kohsuke.stapler.MultipartFormDataParser.FILEUPLOAD_MAX_FILES. Administrators of affected versions should upgrade immediately or apply the workaround by setting this property in the Jenkins startup script [3].

AI Insight generated on May 20, 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.main:jenkins-coreMaven
>= 2.388, < 2.3942.394
org.jenkins-ci.main:jenkins-coreMaven
< 2.375.42.375.4
org.jenkins-ci.main:jenkins-coreMaven
>= 2.376, < 2.387.12.387.1

Affected products

3

Patches

1
b70f4cb5892b

[SECURITY-3030]

https://github.com/jenkinsci/jenkinsDaniel BeckFeb 23, 2023via ghsa
4 files changed · +382 3
  • bom/pom.xml+1 1 modified
    @@ -40,7 +40,7 @@ THE SOFTWARE.
       <properties>
         <asm.version>9.4</asm.version>
         <slf4jVersion>2.0.6</slf4jVersion>
    -    <stapler.version>1770.v0c3dc82ee103</stapler.version>
    +    <stapler.version>1775.v3181508ea_21e</stapler.version>
         <groovy.version>2.4.21</groovy.version>
       </properties>
     
    
  • core/src/main/java/hudson/util/MultipartFormDataParser.java+50 1 modified
    @@ -29,11 +29,15 @@
     import java.util.Map;
     import javax.servlet.ServletException;
     import javax.servlet.http.HttpServletRequest;
    +import org.apache.commons.fileupload.FileCountLimitExceededException;
     import org.apache.commons.fileupload.FileItem;
    +import org.apache.commons.fileupload.FileUploadBase;
     import org.apache.commons.fileupload.FileUploadException;
     import org.apache.commons.fileupload.disk.DiskFileItemFactory;
     import org.apache.commons.fileupload.servlet.ServletFileUpload;
     import org.apache.commons.lang.ArrayUtils;
    +import org.kohsuke.accmod.Restricted;
    +import org.kohsuke.accmod.restrictions.NoExternalUse;
     
     /**
      * Wraps commons file-upload and handles a "multipart/form-data" form submission
    @@ -45,15 +49,60 @@ public class MultipartFormDataParser implements AutoCloseable {
         private final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
         private final Map<String, FileItem> byName = new HashMap<>();
     
    -    public MultipartFormDataParser(HttpServletRequest request) throws ServletException {
    +    /**
    +     * Limits the number of form fields that can be processed in one multipart/form-data request.
    +     * Used to set {@link org.apache.commons.fileupload.servlet.ServletFileUpload#setFileCountMax(long)}.
    +     * Despite the name, this applies to all form fields, not just actual file attachments.
    +     * Set to {@code -1} to disable limits.
    +     */
    +    private static /* nonfinal for Jenkins script console */ int FILEUPLOAD_MAX_FILES = Integer.getInteger(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES", 1000);
    +
    +    /**
    +     * Limits the size (in bytes) of individual fields that can be processed in one multipart/form-data request.
    +     * Used to set {@link org.apache.commons.fileupload.servlet.ServletFileUpload#setFileSizeMax(long)}.
    +     * Despite the name, this applies to all form fields, not just actual file attachments.
    +     * Set to {@code -1} to disable limits.
    +     */
    +    private static /* nonfinal for Jenkins script console */ long FILEUPLOAD_MAX_FILE_SIZE = Long.getLong(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILE_SIZE", -1);
    +
    +    /**
    +     * Limits the total request size (in bytes) that can be processed in one multipart/form-data request.
    +     * Used to set {@link org.apache.commons.fileupload.servlet.ServletFileUpload#setSizeMax(long)}.
    +     * Set to {@code -1} to disable limits.
    +     */
    +    private static /* nonfinal for Jenkins script console */ long FILEUPLOAD_MAX_SIZE = Long.getLong(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_SIZE", -1);
    +
    +    @Restricted(NoExternalUse.class)
    +    public MultipartFormDataParser(HttpServletRequest request, int maxParts, long maxPartSize, long maxSize) throws ServletException {
    +        upload.setFileCountMax(maxParts);
    +        upload.setFileSizeMax(maxPartSize);
    +        upload.setSizeMax(maxSize);
             try {
                 for (FileItem fi : upload.parseRequest(request))
                     byName.put(fi.getFieldName(), fi);
    +        } catch (FileCountLimitExceededException e) {
    +            throw new ServletException("File upload field count limit exceeded. Consider setting the Java system property "
    +                    + MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES to a value greater than " + FILEUPLOAD_MAX_FILES + ", or to -1 to disable this limit.", e);
    +        } catch (FileUploadBase.FileSizeLimitExceededException e) {
    +            throw new ServletException("File upload field size limit exceeded. Consider setting the Java system property "
    +                    + MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILE_SIZE to a value greater than " + FILEUPLOAD_MAX_FILE_SIZE + ", or to -1 to disable this limit.", e);
    +        } catch (FileUploadBase.SizeLimitExceededException e) {
    +            throw new ServletException("File upload total size limit exceeded. Consider setting the Java system property "
    +                    + MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_SIZE to a value greater than " + FILEUPLOAD_MAX_SIZE + ", or to -1 to disable this limit.", e);
             } catch (FileUploadException e) {
                 throw new ServletException(e);
             }
         }
     
    +    @Restricted(NoExternalUse.class)
    +    public MultipartFormDataParser(HttpServletRequest request, int maxParts) throws ServletException {
    +        this(request, maxParts, FILEUPLOAD_MAX_FILE_SIZE, FILEUPLOAD_MAX_SIZE);
    +    }
    +
    +    public MultipartFormDataParser(HttpServletRequest request) throws ServletException {
    +        this(request, FILEUPLOAD_MAX_FILES, FILEUPLOAD_MAX_FILE_SIZE, FILEUPLOAD_MAX_SIZE);
    +    }
    +
         public String get(String key) {
             FileItem fi = byName.get(key);
             if (fi == null)    return null;
    
  • core/src/main/java/jenkins/model/Jenkins.java+1 1 modified
    @@ -4398,7 +4398,7 @@ public void reload() throws IOException, InterruptedException, ReactorException
         @RequirePOST
         public void doDoFingerprintCheck(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
             // Parse the request
    -        try (MultipartFormDataParser p = new MultipartFormDataParser(req)) {
    +        try (MultipartFormDataParser p = new MultipartFormDataParser(req, 10)) {
                 if (isUseCrumbs() && !getCrumbIssuer().validateCrumb(req, p)) {
                     // TODO investigate whether this check can be removed
                     rsp.sendError(HttpServletResponse.SC_FORBIDDEN, "No crumb found");
    
  • test/src/test/java/jenkins/security/Security3030Test.java+330 0 added
    @@ -0,0 +1,330 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2023, CloudBees, Inc.
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a copy
    + * of this software and associated documentation files (the "Software"), to deal
    + * in the Software without restriction, including without limitation the rights
    + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    + * copies of the Software, and to permit persons to whom the Software is
    + * furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included in
    + * all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    + * THE SOFTWARE.
    + */
    +
    +package jenkins.security;
    +
    +import static org.hamcrest.CoreMatchers.containsString;
    +import static org.hamcrest.MatcherAssert.assertThat;
    +import static org.junit.Assert.assertEquals;
    +import static org.junit.Assert.assertNull;
    +
    +import com.gargoylesoftware.htmlunit.HttpMethod;
    +import com.gargoylesoftware.htmlunit.WebRequest;
    +import hudson.ExtensionList;
    +import hudson.model.RootAction;
    +import hudson.util.HttpResponses;
    +import hudson.util.MultipartFormDataParser;
    +import java.io.ByteArrayOutputStream;
    +import java.io.IOException;
    +import java.io.OutputStream;
    +import java.io.OutputStreamWriter;
    +import java.io.PrintWriter;
    +import java.lang.reflect.Field;
    +import java.net.URL;
    +import java.nio.charset.StandardCharsets;
    +import java.util.Random;
    +import javax.servlet.ServletException;
    +import org.apache.commons.fileupload.FileCountLimitExceededException;
    +import org.apache.commons.fileupload.FileUploadBase;
    +import org.apache.commons.fileupload.FileUploadException;
    +import org.junit.Assert;
    +import org.junit.Rule;
    +import org.junit.Test;
    +import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.TestExtension;
    +import org.kohsuke.stapler.HttpResponse;
    +import org.kohsuke.stapler.RequestImpl;
    +import org.kohsuke.stapler.StaplerRequest;
    +import org.kohsuke.stapler.verb.POST;
    +
    +public class Security3030Test {
    +    // TODO Consider parameterizing with Stapler (RequestImpl/StaplerRequestFormAction) + Jenkins (MultipartFormDataParser/MultipartFormDataParserAction)
    +    @Rule
    +    public JenkinsRule j = new JenkinsRule();
    +
    +    @Test
    +    public void fewFilesStapler() throws IOException {
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 20, 10, 1024 * 1024);
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 10, 41, 10);
    +    }
    +
    +    @Test
    +    public void tooManyFilesStapler() throws Exception {
    +        ServletException ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 10, 1000, 20, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 1000, 10, 10, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        try (FieldValue v = withStaticField(RequestImpl.class, "FILEUPLOAD_MAX_FILES", 10_000)) {
    +            assertSubmissionOK(StaplerRequestFormAction.instance(), 1000, 10, 10);
    +            ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 10_000, 10, 10, FileCountLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        }
    +        ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 10, 1000, 20, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 1000, 10, 10, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +    }
    +
    +    @Test
    +    public void tooLargeFilesStapler() throws Exception {
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024);
    +        try (FieldValue v = withStaticField(RequestImpl.class, "FILEUPLOAD_MAX_FILE_SIZE", 1024 * 1024)) {
    +            assertSubmissionOK(StaplerRequestFormAction.instance(), 200, 100, 1024);
    +            ServletException ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024, FileUploadBase.FileSizeLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_FILE_SIZE"));
    +        }
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024);
    +    }
    +
    +    @Test
    +    public void tooLargeSubmissionStapler() throws Exception {
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024);
    +        try (FieldValue v = withStaticField(RequestImpl.class, "FILEUPLOAD_MAX_SIZE", 1024 * 1024)) {
    +            assertSubmissionOK(StaplerRequestFormAction.instance(), 200, 100, 1024);
    +            ServletException ex = assertSubmissionThrows(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024, FileUploadBase.SizeLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(RequestImpl.class.getName() + ".FILEUPLOAD_MAX_SIZE"));
    +        }
    +        assertSubmissionOK(StaplerRequestFormAction.instance(), 1, 50, 10 * 1024 * 1024);
    +    }
    +
    +    @Test
    +    public void fewFilesParser() throws IOException {
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 20, 10, 1024 * 1024);
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 200, 100, 1024);
    +    }
    +
    +    @Test
    +    public void tooManyFilesParser() throws Exception {
    +        ServletException ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 10, 1000, 20, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 1000, 10, 10, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        try (FieldValue v = withStaticField(MultipartFormDataParser.class, "FILEUPLOAD_MAX_FILES", 10_000)) {
    +            assertSubmissionOK(MultipartFormDataParserAction.instance(), 1000, 10, 10);
    +            ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 10_000, 10, 10, FileCountLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        }
    +        ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 10, 1000, 20, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +        ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 1000, 10, 10, FileCountLimitExceededException.class);
    +        assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILES"));
    +    }
    +
    +    @Test
    +    public void tooLargeFilesParser() throws Exception {
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024);
    +        try (FieldValue v = withStaticField(MultipartFormDataParser.class, "FILEUPLOAD_MAX_FILE_SIZE", 1024 * 1024)) {
    +            assertSubmissionOK(MultipartFormDataParserAction.instance(), 200, 100, 1024);
    +            ServletException ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024, FileUploadBase.FileSizeLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_FILE_SIZE"));
    +        }
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024);
    +    }
    +
    +    @Test
    +    public void tooLargeSubmissionParser() throws Exception {
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024);
    +        try (FieldValue v = withStaticField(MultipartFormDataParser.class, "FILEUPLOAD_MAX_SIZE", 1024 * 1024)) {
    +            assertSubmissionOK(MultipartFormDataParserAction.instance(), 200, 100, 1024);
    +            ServletException ex = assertSubmissionThrows(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024, FileUploadBase.SizeLimitExceededException.class);
    +            assertThat(ex.getMessage(), containsString(MultipartFormDataParser.class.getName() + ".FILEUPLOAD_MAX_SIZE"));
    +        }
    +        assertSubmissionOK(MultipartFormDataParserAction.instance(), 1, 50, 10 * 1024 * 1024);
    +    }
    +
    +    // HTTP needs CRLF
    +    private static void println(PrintWriter pw, String s) {
    +        pw.print(s + "\r\n");
    +    }
    +
    +    private static Object getStaticFieldValue(Class<?> clazz, String field) throws IllegalAccessException, NoSuchFieldException {
    +        final Field declaredField = clazz.getDeclaredField(field);
    +        declaredField.setAccessible(true);
    +        return declaredField.get(null);
    +    }
    +
    +    private static void setStaticFieldValue(Class<?> clazz, String field, Object value) throws IllegalAccessException, NoSuchFieldException {
    +        final Field declaredField = clazz.getDeclaredField(field);
    +        declaredField.setAccessible(true);
    +        declaredField.set(null, value);
    +    }
    +
    +    private static FieldValue withStaticField(Class<?> clazz, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
    +        return new FieldValue(clazz, field, value);
    +    }
    +
    +    private static class FieldValue implements AutoCloseable {
    +        private final Class<?> clazz;
    +        private final String field;
    +        private final Object oldValue;
    +
    +        private FieldValue(Class<?> clazz, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
    +            this.clazz = clazz;
    +            this.field = field;
    +            oldValue = getStaticFieldValue(clazz, field);
    +            setStaticFieldValue(clazz, field, value);
    +        }
    +
    +        @Override
    +        public void close() throws Exception {
    +            setStaticFieldValue(clazz, field, oldValue);
    +        }
    +    }
    +
    +    private <T extends Exception> ServletException assertSubmissionThrows(FileUploadAction<T> endpoint, int files, int other, int fileSize, Class<? extends T> expected) throws IOException {
    +        final ServletException actual = testSubmission(endpoint, files, other, fileSize, expected);
    +        assertEquals(actual.getCause().getClass(), expected);
    +        return actual;
    +    }
    +
    +    private <T extends Exception> void assertSubmissionOK(FileUploadAction<T> endpoint, int files, int other, int fileSize) throws IOException {
    +        assertNull(testSubmission(endpoint, files, other, fileSize, null));
    +    }
    +
    +    private <T extends Exception> ServletException testSubmission(FileUploadAction<T> endpoint, int files, int other, int fileSize, Class<? extends T> expected) throws IOException {
    +        endpoint.setExpectedWrapped(expected);
    +        final JenkinsRule.WebClient wc = j.createWebClient();
    +        final URL url = wc.createCrumbedUrl(endpoint.getUrlName() + "/submitMultipart");
    +        WebRequest request = new WebRequest(url, HttpMethod.POST);
    +        final String boundary = "---------------------------" + System.nanoTime();
    +        request.setAdditionalHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
    +        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    +        writeMultipartFormDataBody(baos, boundary, files, other, fileSize);
    +        request.setRequestBody(baos.toString());
    +        wc.getPage(request);
    +        return endpoint.getActual();
    +    }
    +
    +    private static void writeMultipartFormDataBody(OutputStream os, String boundary, int files, int other, int fileSize) throws IOException {
    +        try (OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8); PrintWriter pw = new PrintWriter(osw)) {
    +            final Random random = new Random();
    +            while (files + other > 0) {
    +                println(pw, "--" + boundary);
    +                if (files > other) {
    +                    println(pw, "Content-Disposition: form-data; name=\"file" + files + "\"; filename=\"file" + files + "\"");
    +                    println(pw, "Content-Type: application/octet-stream");
    +                    println(pw, "");
    +                    pw.flush();
    +                    byte[] content = new byte[fileSize];
    +                    random.nextBytes(content);
    +                    os.write(content);
    +                    os.flush();
    +                    println(pw, "");
    +                    files--;
    +                } else {
    +                    println(pw, "Content-Disposition: form-data; name=\"field" + other + "\"");
    +                    println(pw, "");
    +                    println(pw, "Value " + random.nextLong());
    +                    other--;
    +                }
    +            }
    +            println(pw, "--" + boundary + "--");
    +        }
    +    }
    +
    +    public abstract static class FileUploadAction<T extends Exception> implements RootAction {
    +        protected Class<? extends T> expectedWrapped;
    +        protected Throwable actualWrapped;
    +        protected ServletException actual;
    +
    +        @Override
    +        public String getIconFileName() {
    +            return null;
    +        }
    +
    +        @Override
    +        public String getDisplayName() {
    +            return null;
    +        }
    +
    +        @Override
    +        public String getUrlName() {
    +            return getClass().getSimpleName();
    +        }
    +
    +        @POST
    +        public HttpResponse doSubmitMultipart(StaplerRequest req) throws FileUploadException, ServletException, IOException {
    +            if (expectedWrapped == null) {
    +                actualWrapped = null;
    +                actual = null;
    +                return processMultipartAndUnwrap(req);
    +            } else {
    +                actualWrapped = Assert.assertThrows(expectedWrapped, () -> processMultipartAndUnwrap(req));
    +                return HttpResponses.ok();
    +            }
    +        }
    +
    +        private HttpResponse processMultipartAndUnwrap(StaplerRequest req) throws FileUploadException, ServletException, IOException {
    +            try {
    +                return processMultipart(req);
    +            } catch (ServletException ex) {
    +                // unwrap
    +                actual = ex;
    +                final Throwable cause = ex.getCause();
    +                if (cause instanceof FileUploadException) {
    +                    throw (FileUploadException) cause;
    +                }
    +                throw ex;
    +            }
    +        }
    +
    +        protected abstract HttpResponse processMultipart(StaplerRequest req) throws ServletException, IOException;
    +
    +        public void setExpectedWrapped(Class<? extends T> expectedWrapped) {
    +            this.expectedWrapped = expectedWrapped;
    +        }
    +
    +        public Throwable getActualWrapped() {
    +            return actualWrapped;
    +        }
    +
    +        public ServletException getActual() {
    +            return actual;
    +        }
    +    }
    +
    +    @TestExtension
    +    public static class StaplerRequestFormAction extends FileUploadAction<FileUploadException> {
    +        public static StaplerRequestFormAction instance() {
    +            return ExtensionList.lookupSingleton(StaplerRequestFormAction.class);
    +        }
    +
    +        protected HttpResponse processMultipart(StaplerRequest req) throws ServletException, IOException {
    +            req.getFileItem("any-name");
    +            return HttpResponses.ok();
    +        }
    +    }
    +
    +    @TestExtension
    +    public static class MultipartFormDataParserAction extends FileUploadAction<FileUploadException>  {
    +        public static MultipartFormDataParserAction instance() {
    +            return ExtensionList.lookupSingleton(MultipartFormDataParserAction.class);
    +        }
    +
    +        protected HttpResponse processMultipart(StaplerRequest req) throws ServletException {
    +            new MultipartFormDataParser(req);
    +            return HttpResponses.ok();
    +        }
    +    }
    +}
    

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