diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2786a5..e6c92a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). +## Unreleased + +### Fixed + +- Upload size limit is now enforced even when malware scanning is disabled (`scan: false`). Previously, `validateAttachmentSize` used `MAX_FILE_SIZE` as a function reference instead of calling it, and the function returned `-1` (no limit) when scanning was off — allowing arbitrarily large uploads on entities without `@Validation.Maximum`. + ## Version 4.0.0 - 2026-08-03 **BREAKING CHANGE: The attachments plugin comes now without hyperscaler dependencies, please make sure to install them accordingly!** diff --git a/lib/generic-handlers.js b/lib/generic-handlers.js index 05728034..2bf68687 100644 --- a/lib/generic-handlers.js +++ b/lib/generic-handlers.js @@ -1,7 +1,11 @@ const cds = require("@sap/cds") const LOG = cds.log("attachments") const { extname } = require("path") -const { MAX_FILE_SIZE, sizeInBytes, checkMimeTypeMatch } = require("./helper") +const { + DEFAULT_MAX_FILE_SIZE, + sizeInBytes, + checkMimeTypeMatch, +} = require("./helper") const { getMime } = require("./mime") /** @@ -469,8 +473,8 @@ async function validateAttachmentSize(req, validateContentLength = false) { const maxFileSize = ctx.element["@Validation.Maximum"] ? (sizeInBytes(ctx.element["@Validation.Maximum"], req.target.name) ?? - MAX_FILE_SIZE) - : MAX_FILE_SIZE + DEFAULT_MAX_FILE_SIZE) + : DEFAULT_MAX_FILE_SIZE const isInMemory = ctx.content != null && typeof ctx.content.length === "number" diff --git a/lib/helper.js b/lib/helper.js index 58c96af8..e3d7f26f 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -494,15 +494,7 @@ multipliers.PB = multipliers.TB * 1024 multipliers.EB = multipliers.PB * 1024 multipliers.ZB = multipliers.EB * 1024 -/** - * Returns the maximum file size for uploads. - * Returns -1 (no limit) when malware scanning is disabled, otherwise 400MB. - * Evaluated at runtime to support dynamic configuration changes. - * @returns {number} Maximum file size in bytes, or -1 for no limit - */ -function MAX_FILE_SIZE() { - return cds.env.requires?.attachments?.scan == false ? -1 : 419430400 //400 MB in bytes -} +const DEFAULT_MAX_FILE_SIZE = 419430400 // 400 MB in bytes /** * Converts a byte size string into the corresponding number. @@ -515,7 +507,7 @@ function sizeInBytes(size, target) { LOG.warn( `Could not determine the maximum byte size for the content of ${target}, falling back to default.`, ) - return MAX_FILE_SIZE() + return DEFAULT_MAX_FILE_SIZE } if (typeof size === "number") { @@ -527,7 +519,7 @@ function sizeInBytes(size, target) { LOG.warn( `Could not determine the maximum byte size for the content of ${target}, falling back to default.`, ) - return MAX_FILE_SIZE() + return DEFAULT_MAX_FILE_SIZE } const unitMatches = size.toUpperCase().match(/([KMGTPEZ]I?)?B$/) @@ -538,7 +530,7 @@ function sizeInBytes(size, target) { LOG.warn( `Could not determine the maximum byte size for the content of ${target}, falling back to default.`, ) - return MAX_FILE_SIZE() + return DEFAULT_MAX_FILE_SIZE } return value * multipliers[unit] @@ -917,7 +909,7 @@ module.exports = { checkMimeTypeMatch, traverseEntity, buildBackAssocChain, - MAX_FILE_SIZE, + DEFAULT_MAX_FILE_SIZE, getAttachmentKind, handleDuplicates, createSizeCheckHandler, diff --git a/tests/unit/unitTests.test.js b/tests/unit/unitTests.test.js index dd0db93f..4ecef004 100644 --- a/tests/unit/unitTests.test.js +++ b/tests/unit/unitTests.test.js @@ -16,7 +16,7 @@ const { getObjectStoreCredentials, fetchToken, sizeInBytes, - MAX_FILE_SIZE, + DEFAULT_MAX_FILE_SIZE, validateServiceManagerCredentials, } = require("../../lib/helper") @@ -117,12 +117,8 @@ describe("fetchToken", () => { }) describe("max attachment size", () => { - test("should return 400MB in normal scenario", () => { - expect(MAX_FILE_SIZE()).toEqual(400 * 1024 * 1024) - }) - test("should return -1 when scan is disabled", () => { - cds.env.requires.attachments.scan = false - expect(MAX_FILE_SIZE()).toEqual(-1) + test("DEFAULT_MAX_FILE_SIZE is 400MB", () => { + expect(DEFAULT_MAX_FILE_SIZE).toEqual(400 * 1024 * 1024) }) }) @@ -139,15 +135,12 @@ describe("size to byte converter", () => { expect(sizeInBytes(1234)).toEqual(1234) }) - test("conversion of size string returns default MAX_FILE_SIZE if no size could be determined", () => { - // sizeInBytes returns MAX_FILE_SIZE (400MB = 419430400 bytes) as a safe default - // when the size cannot be determined - const MAX_FILE_SIZE = 419430400 // 400MB in bytes - expect(sizeInBytes("ABCDEFG")).toEqual(MAX_FILE_SIZE) - - expect(sizeInBytes(undefined)).toEqual(MAX_FILE_SIZE) - - expect(sizeInBytes({ $edmJson: "Dummy Value" })).toEqual(MAX_FILE_SIZE) + test("conversion of size string returns DEFAULT_MAX_FILE_SIZE if no size could be determined", () => { + expect(sizeInBytes("ABCDEFG")).toEqual(DEFAULT_MAX_FILE_SIZE) + expect(sizeInBytes(undefined)).toEqual(DEFAULT_MAX_FILE_SIZE) + expect(sizeInBytes({ $edmJson: "Dummy Value" })).toEqual( + DEFAULT_MAX_FILE_SIZE, + ) }) })