diff --git a/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/configuration/Registration.java b/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/configuration/Registration.java index 92c477234..884766d50 100644 --- a/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/configuration/Registration.java +++ b/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/configuration/Registration.java @@ -122,11 +122,9 @@ public void eventHandlers(CdsRuntimeConfigurer configurer) { // build malware scanner client, could be null if no service binding is available MalwareScanClient scanClient = buildMalwareScanClient(runtime.getEnvironment()); - // determine default max size based on malware scanner binding availability - String defaultMaxSize = - scanClient != null - ? ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER - : ModifyApplicationHandlerHelper.UNLIMITED_SIZE; + // determine default max size; independent of the scanner binding it stays finite so a missing + // scanner does not allow unbounded uploads. An explicit configuration can raise the limit. + String defaultMaxSize = resolveDefaultMaxSize(runtime.getEnvironment()); AttachmentMalwareScanner malwareScanner = new DefaultAttachmentMalwareScanner(persistenceService, attachmentService, scanClient); @@ -190,6 +188,26 @@ public void eventHandlers(CdsRuntimeConfigurer configurer) { } } + /** + * Resolves the default maximum upload size for attachment content. + * + *

An explicit {@code cds.attachments.maxUploadSize} configuration takes precedence. Otherwise + * a finite default is used regardless of the malware scanner binding, so that a missing scanner + * does not silently allow unbounded uploads. Consuming apps can still override the limit per + * entity via {@code @Validation.Maximum}. + * + * @param environment the {@link CdsEnvironment} to read configuration from + * @return the default maximum upload size as a size string (e.g. {@code "400MB"}) + */ + static String resolveDefaultMaxSize(CdsEnvironment environment) { + String configured = + environment.getProperty("cds.attachments.maxUploadSize", String.class, null); + if (configured != null && !configured.isBlank()) { + return configured; + } + return ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE; + } + /** * Builds the {@link MalwareScanClient malware scanner client} based on the service binding. * diff --git a/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelper.java b/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelper.java index 2c315bbe9..ea7723286 100644 --- a/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelper.java +++ b/cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelper.java @@ -23,11 +23,14 @@ public final class ModifyApplicationHandlerHelper { - /** Default max size when malware scanner binding is present (400 MB). */ - public static final String DEFAULT_SIZE_WITH_SCANNER = "400MB"; - - /** Effectively unlimited max size when no malware scanner binding is present. */ - public static final String UNLIMITED_SIZE = String.valueOf(Long.MAX_VALUE); + /** + * Default maximum upload size for attachment content (400 MB). A finite default is used + * regardless of the malware scanner binding so that a missing scanner does not silently allow + * unbounded uploads. It can be raised via the {@code cds.attachments.maxUploadSize} property or a + * per-entity {@code @Validation.Maximum} annotation, and matches the SAP Malware Scanning Service + * limit when a scanner is present. + */ + public static final String DEFAULT_MAX_UPLOAD_SIZE = "400MB"; /** * Handles attachments for entities. diff --git a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/configuration/RegistrationTest.java b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/configuration/RegistrationTest.java index 3a4726a50..7edfecd9e 100644 --- a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/configuration/RegistrationTest.java +++ b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/configuration/RegistrationTest.java @@ -15,6 +15,7 @@ import com.sap.cds.feature.attachments.handler.applicationservice.DeleteAttachmentsHandler; import com.sap.cds.feature.attachments.handler.applicationservice.ReadAttachmentsHandler; import com.sap.cds.feature.attachments.handler.applicationservice.UpdateAttachmentsHandler; +import com.sap.cds.feature.attachments.handler.applicationservice.helper.ModifyApplicationHandlerHelper; import com.sap.cds.feature.attachments.handler.draftservice.DraftActiveAttachmentsHandler; import com.sap.cds.feature.attachments.handler.draftservice.DraftCancelAttachmentsHandler; import com.sap.cds.feature.attachments.handler.draftservice.DraftPatchAttachmentsHandler; @@ -253,6 +254,35 @@ void environmentHandlesNullExistingPaths() { "../target/cds/com.sap.cds/cds-feature-attachments/**"); } + @Test + void resolveDefaultMaxSize_returnsFiniteDefaultWhenNoConfig() { + CdsEnvironment environment = mock(CdsEnvironment.class); + when(environment.getProperty("cds.attachments.maxUploadSize", String.class, null)) + .thenReturn(null); + + assertThat(Registration.resolveDefaultMaxSize(environment)) + .isEqualTo(ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE); + } + + @Test + void resolveDefaultMaxSize_ignoresBlankConfig() { + CdsEnvironment environment = mock(CdsEnvironment.class); + when(environment.getProperty("cds.attachments.maxUploadSize", String.class, null)) + .thenReturn(" "); + + assertThat(Registration.resolveDefaultMaxSize(environment)) + .isEqualTo(ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE); + } + + @Test + void resolveDefaultMaxSize_honorsConfiguredOverride() { + CdsEnvironment environment = mock(CdsEnvironment.class); + when(environment.getProperty("cds.attachments.maxUploadSize", String.class, null)) + .thenReturn("1GB"); + + assertThat(Registration.resolveDefaultMaxSize(environment)).isEqualTo("1GB"); + } + private void isHandlerForClassIncluded( List handlers, Class includedClass) { var isHandlerIncluded = diff --git a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/CreateAttachmentsHandlerTest.java b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/CreateAttachmentsHandlerTest.java index 37ed28c8a..d93310607 100644 --- a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/CreateAttachmentsHandlerTest.java +++ b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/CreateAttachmentsHandlerTest.java @@ -77,7 +77,7 @@ void setup() { new CreateAttachmentsHandler( eventFactory, storageReader, - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER, + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE, runtime); createContext = mock(CdsCreateEventContext.class); diff --git a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/UpdateAttachmentsHandlerTest.java b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/UpdateAttachmentsHandlerTest.java index 8ea36c180..f41c39267 100644 --- a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/UpdateAttachmentsHandlerTest.java +++ b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/UpdateAttachmentsHandlerTest.java @@ -85,7 +85,7 @@ void setup() { attachmentsReader, attachmentService, storageReader, - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER); + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE); event = mock(ModifyAttachmentEvent.class); updateContext = mock(CdsUpdateEventContext.class); diff --git a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelperTest.java b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelperTest.java index 3c529434a..903db24dd 100644 --- a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelperTest.java +++ b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/applicationservice/helper/ModifyApplicationHandlerHelperTest.java @@ -92,7 +92,7 @@ void serviceExceptionDueToContentLength() { eventContext, path, attachment.getContent(), - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER)); + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE)); assertThat(exception.getErrorStatus()).isEqualTo(ExtendedErrorStatuses.CONTENT_TOO_LARGE); } @@ -147,7 +147,7 @@ void serviceExceptionDueToLimitExceeded() { eventContext, path, content, - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER)); + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE)); assertThat(exception.getErrorStatus()).isEqualTo(ExtendedErrorStatuses.CONTENT_TOO_LARGE); } @@ -179,7 +179,7 @@ void defaultValMaxValueUsed() { eventContext, path, content, - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER)); + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE)); } @Test @@ -211,7 +211,7 @@ void malformedContentLengthHeader() { eventContext, path, content, - ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER)); + ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE)); assertThat(exception.getErrorStatus()).isEqualTo(ErrorStatuses.BAD_REQUEST); } diff --git a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/DraftPatchAttachmentsHandlerTest.java b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/DraftPatchAttachmentsHandlerTest.java index 035dd766b..746767750 100644 --- a/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/DraftPatchAttachmentsHandlerTest.java +++ b/cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/DraftPatchAttachmentsHandlerTest.java @@ -63,7 +63,7 @@ void setup() { eventFactory = mock(ModifyAttachmentEventFactory.class); cut = new DraftPatchAttachmentsHandler( - persistence, eventFactory, ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER); + persistence, eventFactory, ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE); eventContext = mock(DraftPatchEventContext.class); event = mock(ModifyAttachmentEvent.class); when(eventFactory.getEvent(any(), any(), any())).thenReturn(event);