Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -190,6 +188,26 @@ public void eventHandlers(CdsRuntimeConfigurer configurer) {
}
}

/**
* Resolves the default maximum upload size for attachment content.
*
* <p>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;
}
Comment on lines +202 to +209

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best Practice: resolveDefaultMaxSize accepts any non-blank configured value without validating its format, but an invalid value (e.g. "400 megabytes" or "abc") is only caught later at upload time when FileSizeUtils.parseFileSizeToBytes throws IllegalArgumentException, turning a configuration error into a runtime crash on the first upload attempt.

Consider validating the configured value at resolution time by calling FileSizeUtils.parseFileSizeToBytes and falling back to the default (or re-throwing with a clear message) if it is malformed:

Suggested change
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;
}
static String resolveDefaultMaxSize(CdsEnvironment environment) {
String configured =
environment.getProperty("cds.attachments.maxUploadSize", String.class, null);
if (configured != null && !configured.isBlank()) {
try {
FileSizeUtils.parseFileSizeToBytes(configured); // validate format eagerly
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
"Invalid value for cds.attachments.maxUploadSize: '" + configured + "'", e);
}
return configured;
}
return ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE;
}

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful


/**
* Builds the {@link MalwareScanClient malware scanner client} based on the service binding.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<EventHandler> handlers, Class<? extends EventHandler> includedClass) {
var isHandlerIncluded =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -179,7 +179,7 @@ void defaultValMaxValueUsed() {
eventContext,
path,
content,
ModifyApplicationHandlerHelper.DEFAULT_SIZE_WITH_SCANNER));
ModifyApplicationHandlerHelper.DEFAULT_MAX_UPLOAD_SIZE));
}

@Test
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading