VYPR
Medium severity6.4OSV Advisory· Published Apr 22, 2025· Updated Apr 15, 2026

CVE-2025-32961

CVE-2025-32961

Description

The Cuba JPA web API enables loading and saving any entities defined in the application data model by sending simple HTTP requests. Prior to version 1.1.1, the input parameter, which consists of a file path and name, can be manipulated to return the Content-Type header with text/html if the name part ends with .html. This could allow malicious JavaScript code to be executed in the browser. For a successful attack, a malicious file needs to be uploaded beforehand. This issue has been patched in version 1.1.1. A workaround is provided on the Jmix documentation website.

AI Insight

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

CVE-2025-32961 is a stored XSS vulnerability in the Cuba JPA web API's /files endpoint, allowing execution of malicious JavaScript via manipulated file names ending in .html.

Vulnerability

The Cuba JPA web API's /files endpoint returns files with a Content-Type header derived from the file extension. If the file name ends with .html, the server responds with text/html, enabling cross-site scripting (XSS). This occurs because the endpoint does not validate or restrict the file extension before setting the Content-Type [1].

Exploitation

An attacker must first upload a malicious file (e.g., containing JavaScript) to the file storage. Then, by sending a request to /files with a manipulated file path or name that ends with .html, the server returns the malicious content with a text/html Content-Type. The browser then executes the script in the context of the application [1].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the victim's browser. This can lead to session hijacking, data theft, or unauthorized actions performed on behalf of the authenticated user [1].

Mitigation

The vulnerability is patched in version 1.1.1. The fix introduces a configurable list of safe file extensions for inline viewing (default: jpg, png, jpeg, pdf) [4]. For those unable to upgrade, a workaround is to disable the /files endpoint entirely, as documented on the Jmix website [1].

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
com.haulmont.addon.jpawebapi:jpawebapi-jpawebapiMaven
< 1.1.11.1.1

Affected products

1

Patches

2
78b837d7e2b1

REST files fix #15

https://github.com/cuba-platform/jpawebapiIvan GavrilovMar 14, 2025via ghsa
2 files changed · +38 0
  • modules/jpawebapi/src/com/haulmont/addon/jpawebapi/api/config/JpaWebApiConfig.java+13 0 modified
    @@ -21,8 +21,13 @@
     import com.haulmont.cuba.core.config.Property;
     import com.haulmont.cuba.core.config.Source;
     import com.haulmont.cuba.core.config.SourceType;
    +import com.haulmont.cuba.core.config.defaults.Default;
     import com.haulmont.cuba.core.config.defaults.DefaultBoolean;
     import com.haulmont.cuba.core.config.defaults.DefaultInt;
    +import com.haulmont.cuba.core.config.type.CommaSeparatedStringListTypeFactory;
    +import com.haulmont.cuba.core.config.type.Factory;
    +
    +import java.util.List;
     
     /**
      * JPA WEB API config.
    @@ -60,4 +65,12 @@ public interface JpaWebApiConfig extends Config {
          */
         @Property("jpawebapi.mapping.url")
         String getServletMapping();
    +
    +    /**
    +     * File extensions that can be opened for viewing in a browser by replying with 'Content-Disposition=inline' header.
    +     */
    +    @Property("cuba.rest.inlineEnabledFileExtensions")
    +    @Factory(factory = CommaSeparatedStringListTypeFactory.class)
    +    @Default("jpg, png, jpeg, pdf")
    +    List<String> getInlineEnabledFileExtensions();
     }
    
  • modules/jpawebapi/src/com/haulmont/addon/jpawebapi/api/controller/RestFileDownloadController.java+25 0 modified
    @@ -17,6 +17,7 @@
     
     package com.haulmont.addon.jpawebapi.api.controller;
     
    +import com.haulmont.addon.jpawebapi.api.config.JpaWebApiConfig;
     import com.haulmont.bali.util.URLEncodeUtils;
     import com.haulmont.cuba.client.ClientConfig;
     import com.haulmont.cuba.core.app.DataService;
    @@ -30,6 +31,7 @@
     import com.haulmont.cuba.security.global.NoUserSessionException;
     import com.haulmont.cuba.security.global.UserSession;
     import org.apache.commons.io.IOUtils;
    +import org.apache.commons.lang3.BooleanUtils;
     import org.apache.commons.lang3.StringUtils;
     import org.apache.http.HttpEntity;
     import org.apache.http.HttpResponse;
    @@ -52,6 +54,7 @@
     import javax.servlet.http.HttpServletResponse;
     import java.io.IOException;
     import java.io.InputStream;
    +import java.util.List;
     import java.util.UUID;
     
     /**
    @@ -76,6 +79,9 @@ public class RestFileDownloadController {
         @Inject
         protected ClientConfig clientConfig;
     
    +    @Inject
    +    protected JpaWebApiConfig jpaWebApiConfig;
    +
         protected String fileDownloadContext;
     
         @RequestMapping(value = "/download", method = RequestMethod.GET)
    @@ -113,6 +119,9 @@ public ModelAndView download(HttpServletRequest request, HttpServletResponse res
                 response.setHeader("Pragma", "no-cache");
     
                 boolean attach = Boolean.valueOf(request.getParameter("a"));
    +
    +            attach = resolveAttachmentValue(attach, fd);
    +
                 response.setHeader("Content-Disposition", (attach ? "attachment" : "inline")
                         + "; filename=" + fileName);
     
    @@ -179,6 +188,22 @@ protected void writeResponse(HttpServletResponse response, UserSession userSessi
             }
         }
     
    +    protected boolean resolveAttachmentValue(Boolean attachmentRequestParameterValue, FileDescriptor fileDescriptor) {
    +        if (BooleanUtils.isTrue(attachmentRequestParameterValue)) {
    +            return true;
    +        }
    +
    +        String extension = fileDescriptor.getExtension();
    +        if (StringUtils.isEmpty(extension)) {
    +            // No extension - just download
    +            return true;
    +        } else {
    +            // Check if file is allowed to be opened inline
    +            List<String> inlineEnabledFileExtensions = jpaWebApiConfig.getInlineEnabledFileExtensions();
    +            return !inlineEnabledFileExtensions.contains(StringUtils.lowerCase(extension));
    +        }
    +    }
    +
         @Nullable
         protected String failAndGetNextUrl(Object context, HttpServletResponse response) throws IOException {
             serverSelector.fail(context);
    

Vulnerability mechanics

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

References

7

News mentions

0

No linked articles in our index yet.