CVE-2025-43752
Description
Liferay Portal 7.4.0 through 7.4.3.132, and Liferay DXP 2025.Q1.0 through 2025.Q1.4, 2024.Q4.0 through 2024.Q4.7, 2024.Q3.1 through 2024.Q3.13, 2024.Q2.0 through 2024.Q2.13, 2024.Q1.1 through 2024.Q1.15 and 7.4 GA through update 92 allow users to upload an unlimited amount of files through the object entries attachment fields, the files are stored in the document_library allowing an attacker to cause a potential DDoS.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
com.liferay.portal:release.portal.bomMaven | >= 7.4.0-ga1, <= 7.4.3.132-ga132 | — |
Affected products
2- Liferay/DXPv5Range: 7.4.13
Patches
345dda30252d8LPD-49638 Add playwright test
4 files changed · +141 −0
modules/test/playwright/helpers/HeadlessDeliveryApiHelper.ts+6 −0 modified@@ -108,6 +108,12 @@ export class HeadlessDeliveryApiHelper { ); } + async getDocument(documentId: string) { + return this.apiHelpers.get( + `${this.apiHelpers.baseUrl}${this.basePath}/documents/${documentId}` + ); + } + async getSiteDocumentsPage(siteId: string, sort: string = 'id') { return this.apiHelpers.get( `${this.apiHelpers.baseUrl}${this.basePath}/sites/${siteId}/documents?sort=${sort}`
modules/test/playwright/pages/object-web/object-entries/ViewObjectEntriesPage.ts+15 −0 modified@@ -5,6 +5,7 @@ import {ObjectField} from '@liferay/object-admin-rest-client-js'; import {FrameLocator, Locator, Page, expect} from '@playwright/test'; +import path from 'path'; import {PORTLET_URLS} from '../../../utils/portletUrls'; @@ -181,6 +182,20 @@ export class ViewObjectEntriesPage { .click(); } + async selectFileFromUserComputer(dirName: string, fileName: string) { + const fileChooserPromise = this.page.waitForEvent('filechooser'); + + await this.selectFileButton.click(); + + const fileChooser = await fileChooserPromise; + + await fileChooser.setFiles( + path.join(dirName, 'dependencies', fileName) + ); + + await this.page.getByText(fileName).waitFor({state: 'visible'}); + } + async goto( objectDefinitionClassName: string, regionalCode?: string,
modules/test/playwright/tests/object-web/dependencies/astronaut.png+0 −0 addedmodules/test/playwright/tests/object-web/objectEntries.spec.ts+120 −0 modified@@ -1455,6 +1455,126 @@ test.describe('Manage object entries through View Object Entries', () => { 'Entry A' ); }); + + test('Verify that temporary files are deleted from the database if the object creation is not completed', async ({ + apiHelpers, + page, + viewObjectEntriesPage, + }) => { + + // Create object definition with attachment object field + + const objectDefinition = + await apiHelpers.objectAdmin.postRandomObjectDefinition({ + objectFields: [mockedObjectFields.attachmentFieldUserComputer], + objectFolderExternalReferenceCode: 'default', + status: {code: 0}, + }); + + apiHelpers.data.push({ + id: objectDefinition.id, + type: 'objectDefinition', + }); + + await viewObjectEntriesPage.goto(objectDefinition.className); + + await viewObjectEntriesPage.clickAddObjectEntry(objectDefinition.name); + + // Upload first file from user computer + + await viewObjectEntriesPage.selectFileFromUserComputer( + __dirname, + 'sampleFile.txt' + ); + + const fileEntryId1 = await page.getAttribute( + 'input[data-field-name^="testAttachment"]', + 'value' + ); + + expect( + await apiHelpers.headlessDelivery.getDocument(fileEntryId1) + ).toEqual( + expect.objectContaining({ + id: Number(fileEntryId1), + }) + ); + + // Verify that the first file is removed after the second file is uploaded + + await viewObjectEntriesPage.selectFileFromUserComputer( + __dirname, + 'astronaut.png' + ); + + expect( + await apiHelpers.headlessDelivery.getDocument(fileEntryId1) + ).toEqual({status: 'NOT_FOUND'}); + + const fileEntryId2 = await page.getAttribute( + 'input[data-field-name^="testAttachment"]', + 'value' + ); + + expect( + await apiHelpers.headlessDelivery.getDocument(fileEntryId2) + ).toEqual( + expect.objectContaining({ + id: Number(fileEntryId2), + }) + ); + + // Verify that the delete button removes the second file + + await viewObjectEntriesPage.deleteFileButton.click(); + + expect( + await apiHelpers.headlessDelivery.getDocument(fileEntryId2) + ).toEqual({status: 'NOT_FOUND'}); + + // Verify that the file is removed after page reload + + await viewObjectEntriesPage.selectFileFromUserComputer( + __dirname, + 'sampleFile.txt' + ); + + const fileEntryId3 = await page.getAttribute( + 'input[data-field-name^="testAttachment"]', + 'value' + ); + + await page.reload(); + + expect( + await apiHelpers.headlessDelivery.getDocument(fileEntryId3) + ).toEqual({status: 'NOT_FOUND'}); + + // Verify that the file is saved successfully when clicking submit + + await viewObjectEntriesPage.selectFileFromUserComputer( + __dirname, + 'astronaut.png' + ); + + await viewObjectEntriesPage.saveObjectEntryButton.click(); + + await expect(viewObjectEntriesPage.successMessage).toBeVisible(); + await expect( + viewObjectEntriesPage.page.getByText('astronaut.png') + ).toBeVisible(); + + await viewObjectEntriesPage.selectFileFromUserComputer( + __dirname, + 'sampleFile.txt' + ); + + await page.reload(); + + await expect( + viewObjectEntriesPage.page.getByText('astronaut.png') + ).toBeVisible(); + }); }); test.describe('Manage object entries through Workflow', () => {
f3e4723acdf1LPD-49638 Delete current file entry when the user clicks the delete button, selects a new file, or refreshes the page
3 files changed · +68 −2
modules/apps/object/object-dynamic-data-mapping-form-field-type/src/main/resources/META-INF/resources/js/Attachment/AttachmentBase.tsx+7 −1 modified@@ -21,6 +21,7 @@ import {LocalizedValue} from 'dynamic-data-mapping-form-field-type/src/main/reso export type AttachmentFile = { contentURL: string; + fileEntryId: string; title: string; }; @@ -90,6 +91,7 @@ export default function AttachmentBase({ onAttachmentChange( { contentURL: selectedItemValue.url, + fileEntryId: selectedItemValue.fileEntryId, title: selectedItemValue.title, }, selectedItemValue.fileEntryId @@ -133,7 +135,11 @@ export default function AttachmentBase({ } else { onAttachmentChange( - {contentURL: file.contentURL, title: file.title}, + { + contentURL: file.contentURL, + fileEntryId: file.fileEntryId, + title: file.title, + }, file.fileEntryId ); }
modules/apps/object/object-dynamic-data-mapping-form-field-type/src/main/resources/META-INF/resources/js/Attachment/Attachment.tsx+59 −1 modified@@ -3,6 +3,7 @@ * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06 */ +import {useConfig} from 'data-engine-js-components-web'; import {ReactFieldBase as FieldBase} from 'dynamic-data-mapping-form-field-type'; import { FieldChangeEventHandler, @@ -12,7 +13,8 @@ import { AvailableLocale, EditingLocale, } from 'dynamic-data-mapping-form-field-type/src/main/resources/META-INF/resources/util/localizable/LocalesDropdown'; -import React, {useState} from 'react'; +import {fetch} from 'frontend-js-web'; +import React, {useCallback, useEffect, useState} from 'react'; import AttachmentBase, { AttachmentBaseProps, @@ -25,6 +27,7 @@ export interface AttachmentProps availableLocales: AvailableLocale[]; contentURL?: string; defaultLocale: EditingLocale; + deleteURL?: string; fieldName: string; fileEntryProperties: AttachmentFile | LocalizedValue<AttachmentFile>; localizedObjectField: boolean; @@ -34,6 +37,7 @@ export interface AttachmentProps export default function Attachment({ contentURL, + deleteURL, fileEntryProperties, localizedObjectField, onChange, @@ -46,6 +50,8 @@ export default function Attachment({ const isLocalizedObjectField: boolean = Liferay.FeatureFlags['LPD-32050'] && !!localizedObjectField; + const {portletNamespace} = useConfig(); + const getAttachment = () => { if (Liferay.FeatureFlags['LPD-32050']) { return fileEntryProperties; @@ -61,17 +67,69 @@ export default function Attachment({ getAttachment() as AttachmentFile | null ); const [error, setError] = useState({}); + const [submitButtonClicked, setSubmitButtonClicked] = useState(false); + + const deleteFileEntry = useCallback(async () => { + if (!attachment || !deleteURL) { + return; + } + + const {fileEntryId} = attachment; + + if (!fileEntryId) { + return; + } + + const formData = new FormData(); + + formData.append(`${portletNamespace}fileEntryId`, fileEntryId); + + await fetch(deleteURL, { + body: formData, + method: 'POST', + }); + }, [attachment, deleteURL, portletNamespace]); + + useEffect(() => { + window.onbeforeunload = function () { + if (!submitButtonClicked) { + deleteFileEntry(); + } + }; + + return () => { + window.onbeforeunload = null; + }; + }, [deleteFileEntry, submitButtonClicked]); + + useEffect(() => { + Liferay.on( + 'submitButtonClicked', + + () => { + setSubmitButtonClicked(true); + } + ); + + return () => { + Liferay.detach('submitButtonClicked'); + }; + }, []); const handleAttachmentChange = ( attachmentValue: AttachmentFile, fileId: string ) => { + deleteFileEntry(); + setAttachment(attachmentValue); onChange({target: {value: fileId}}); }; const handleDelete = () => { + deleteFileEntry(); + setAttachment(null); onChange({target: {value: ''}}); // TODO: fix backend to support null
modules/apps/object/object-web/src/main/resources/META-INF/resources/object_entries/object_entry/form.jsp+2 −0 modified@@ -252,6 +252,8 @@ portletDisplay.setURLBack(backURL); method: externalReferenceCode ? 'PATCH' : 'POST', }) .then((response) => { + Liferay.fire('submitButtonClicked'); + if (response.status === 401) { window.location.reload(); }
fffed67b3fd1LPD-49638 Add new MVCActionCommand to delete file entry
3 files changed · +101 −0
modules/apps/object/object-dynamic-data-mapping-form-field-type/src/main/java/com/liferay/object/dynamic/data/mapping/form/field/type/internal/attachment/AttachmentDDMFormFieldTemplateContextContributor.java+23 −0 modified@@ -17,6 +17,7 @@ import com.liferay.item.selector.criteria.FileEntryItemSelectorReturnType; import com.liferay.item.selector.criteria.file.criterion.FileItemSelectorCriterion; import com.liferay.object.configuration.ObjectConfiguration; +import com.liferay.object.constants.ObjectFieldSettingConstants; import com.liferay.object.dynamic.data.mapping.form.field.type.constants.ObjectDDMFormFieldTypeConstants; import com.liferay.petra.string.StringPool; import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; @@ -73,6 +74,28 @@ public Map<String, Object> getParameters( Map<String, Object> parameters = HashMapBuilder.<String, Object>put( "acceptedFileExtensions", ddmFormField.getProperty("acceptedFileExtensions") + ).put( + "deleteURL", + () -> { + if (!Objects.equals( + ddmFormField.getProperty("fileSource"), + ObjectFieldSettingConstants.VALUE_USER_COMPUTER)) { + + return null; + } + + RequestBackedPortletURLFactory requestBackedPortletURLFactory = + RequestBackedPortletURLFactoryUtil.create( + ddmFormFieldRenderingContext.getHttpServletRequest()); + + return PortletURLBuilder.create( + requestBackedPortletURLFactory.createActionURL( + GetterUtil.getString( + ddmFormField.getProperty("portletId"))) + ).setActionName( + "/object_entries/delete_attachment" + ).buildString(); + } ).put( "fileSource", ddmFormField.getProperty("fileSource") ).put(
modules/apps/object/object-web/src/main/java/com/liferay/object/web/internal/deployer/ObjectDefinitionDeployerImpl.java+14 −0 modified@@ -15,6 +15,7 @@ import com.liferay.document.library.kernel.exception.FileSizeException; import com.liferay.document.library.kernel.exception.InvalidFileException; import com.liferay.document.library.kernel.service.DLAppLocalService; +import com.liferay.document.library.kernel.service.DLFileEntryLocalService; import com.liferay.document.library.util.DLURLHelper; import com.liferay.friendly.url.info.item.provider.InfoItemFriendlyURLProvider; import com.liferay.friendly.url.info.item.updater.InfoItemFriendlyURLUpdater; @@ -111,6 +112,7 @@ import com.liferay.object.web.internal.object.entries.frontend.data.set.filter.factory.ObjectFieldFDSFilterFactoryRegistry; import com.liferay.object.web.internal.object.entries.frontend.data.set.view.table.ObjectEntriesTableFDSView; import com.liferay.object.web.internal.object.entries.portlet.ObjectEntriesPortlet; +import com.liferay.object.web.internal.object.entries.portlet.action.DeleteAttachmentMVCActionCommand; import com.liferay.object.web.internal.object.entries.portlet.action.EditObjectEntryMVCActionCommand; import com.liferay.object.web.internal.object.entries.portlet.action.EditObjectEntryMVCRenderCommand; import com.liferay.object.web.internal.object.entries.portlet.action.EditObjectEntryRelatedModelMVCActionCommand; @@ -527,6 +529,15 @@ public List<ServiceRegistration<?>> deploy( ).put( "javax.portlet.version", "3.0" ).build()), + _bundleContext.registerService( + MVCActionCommand.class, + new DeleteAttachmentMVCActionCommand( + _dlFileEntryLocalService, objectDefinition), + HashMapDictionaryBuilder.<String, Object>put( + "javax.portlet.name", objectDefinition.getPortletId() + ).put( + "mvc.command.name", "/object_entries/delete_attachment" + ).build()), _bundleContext.registerService( MVCActionCommand.class, new EditObjectEntryMVCActionCommand( @@ -726,6 +737,9 @@ private String _getResourceName(ObjectDefinition objectDefinition) { @Reference private DLAppLocalService _dlAppLocalService; + @Reference + private DLFileEntryLocalService _dlFileEntryLocalService; + @Reference private DLURLHelper _dlURLHelper;
modules/apps/object/object-web/src/main/java/com/liferay/object/web/internal/object/entries/portlet/action/DeleteAttachmentMVCActionCommand.java+64 −0 added@@ -0,0 +1,64 @@ +/** + * SPDX-FileCopyrightText: (c) 2025 Liferay, Inc. https://liferay.com + * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06 + */ + +package com.liferay.object.web.internal.object.entries.portlet.action; + +import com.liferay.document.library.kernel.model.DLFileEntry; +import com.liferay.document.library.kernel.model.DLFolder; +import com.liferay.document.library.kernel.service.DLFileEntryLocalService; +import com.liferay.object.model.ObjectDefinition; +import com.liferay.portal.kernel.portlet.bridges.mvc.BaseMVCActionCommand; +import com.liferay.portal.kernel.util.ParamUtil; +import com.liferay.portal.kernel.util.StringUtil; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; + +/** + * @author Carolina Barbosa + */ +public class DeleteAttachmentMVCActionCommand extends BaseMVCActionCommand { + + public DeleteAttachmentMVCActionCommand( + DLFileEntryLocalService dlFileEntryLocalService, + ObjectDefinition objectDefinition) { + + _dlFileEntryLocalService = dlFileEntryLocalService; + _objectDefinition = objectDefinition; + } + + @Override + protected void doProcessAction( + ActionRequest actionRequest, ActionResponse actionResponse) + throws Exception { + + long fileEntryId = ParamUtil.getLong(actionRequest, "fileEntryId"); + + if (fileEntryId == 0) { + return; + } + + DLFileEntry dlFileEntry = _dlFileEntryLocalService.fetchDLFileEntry( + fileEntryId); + + if (dlFileEntry == null) { + return; + } + + DLFolder dlFolder = dlFileEntry.getFolder(); + + if (!StringUtil.equals( + dlFolder.getName(), _objectDefinition.getPortletId())) { + + return; + } + + _dlFileEntryLocalService.deleteFileEntry(fileEntryId); + } + + private final DLFileEntryLocalService _dlFileEntryLocalService; + private final ObjectDefinition _objectDefinition; + +} \ No newline at end of file
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-qpp6-f3qj-rggqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-43752ghsaADVISORY
- github.com/liferay/liferay-portal/commit/45dda30252d83912307491d8ed8802577871fa25ghsaWEB
- github.com/liferay/liferay-portal/commit/f3e4723acdf15d3f690d401d6eb6a5653e5be391ghsaWEB
- github.com/liferay/liferay-portal/commit/fffed67b3fd1cc6071fd25a9b104b7691ffea2f8ghsaWEB
- liferay.atlassian.net/browse/LPE-18188ghsaWEB
- liferay.dev/portal/security/known-vulnerabilities/-/asset_publisher/jekt/content/CVE-2025-43752ghsaWEB
News mentions
0No linked articles in our index yet.