From 5bf3a532ef31b0c3dc44362ae93d156d93d02bab Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Wed, 27 May 2026 17:32:44 -0700 Subject: [PATCH 1/3] CLDSRV-909: Reject CopyObject when source exceeds 5 GiB --- lib/api/objectCopy.js | 11 ++++++++ tests/unit/api/objectCopy.js | 50 +++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/lib/api/objectCopy.js b/lib/api/objectCopy.js index e27d85afca..0993daf985 100644 --- a/lib/api/objectCopy.js +++ b/lib/api/objectCopy.js @@ -641,6 +641,17 @@ function objectCopy(authInfo, request, sourceBucket, sourceObject, sourceVersion request.sourceServerAccessLog && (request.sourceServerAccessLog.error = err); return next(err, destBucketMD); } + const sourceSize = parseInt(sourceObjMD['content-length'], 10); + if (sourceSize > constants.maximumAllowedUploadSize) { + log.debug('copy source object too large', { sourceSize }); + const err = errorInstances.InvalidRequest.customizeDescription( + 'The specified copy source is larger than the maximum ' + + `allowable size for a copy source: ${constants.maximumAllowedUploadSize}`, + ); + // eslint-disable-next-line no-param-reassign + request.sourceServerAccessLog && (request.sourceServerAccessLog.error = err); + return next(err, destBucketMD); + } const headerValResult = validateHeaders( request.headers, sourceObjMD['last-modified'], diff --git a/tests/unit/api/objectCopy.js b/tests/unit/api/objectCopy.js index bf1c180194..d12f6f9415 100644 --- a/tests/unit/api/objectCopy.js +++ b/tests/unit/api/objectCopy.js @@ -16,7 +16,8 @@ const mpuUtils = require('../utils/mpuUtils'); const metadata = require('../metadataswitch'); const { data } = require('../../../lib/data/wrapper'); const kms = require('../../../lib/kms/wrapper'); -const { objectLocationConstraintHeader } = require('../../../constants'); +const constants = require('../../../constants'); +const { objectLocationConstraintHeader } = constants; const { algorithms } = require('../../../lib/api/apiUtils/integrity/validateChecksums'); const { fakeMetadataArchive } = require('../../functional/aws-node-sdk/test/utils/init'); const { config } = require('../../../lib/Config'); @@ -1865,3 +1866,50 @@ describe('_orphanedDataLocations', () => { assert.deepStrictEqual(orphanedDataLocations(['oldKey'], [{ key: 'newKey' }]), ['oldKey']); }); }); + +describe('objectCopy source size limit', () => { + const testPutObjectRequest = versioningTestUtils.createPutObjectRequest(sourceBucketName, objectKey, objData[0]); + const sourceSize = objData[0].length; + let originalMaximumUploadSize; + + before(done => { + cleanup(); + originalMaximumUploadSize = constants.maximumAllowedUploadSize; + async.series( + [ + callback => bucketPut(authInfo, putDestBucketRequest, log, callback), + callback => bucketPut(authInfo, putSourceBucketRequest, log, callback), + callback => objectPut(authInfo, testPutObjectRequest, undefined, log, callback), + ], + done, + ); + }); + + after(() => { + constants.maximumAllowedUploadSize = originalMaximumUploadSize; + cleanup(); + }); + + it('should allow CopyObject when source size equals the limit', done => { + constants.maximumAllowedUploadSize = sourceSize; + const testObjectCopyRequest = _createObjectCopyRequest(destBucketName); + objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, undefined, log, err => { + assert.ifError(err); + done(); + }); + }); + + it('should reject CopyObject when source size exceeds the limit', done => { + constants.maximumAllowedUploadSize = sourceSize - 1; + const testObjectCopyRequest = _createObjectCopyRequest(destBucketName); + objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, undefined, log, err => { + assert(err); + assert.strictEqual(err.is.InvalidRequest, true); + assert.match( + err.description, + /The specified copy source is larger than the maximum allowable size for a copy source/, + ); + done(); + }); + }); +}); From 921e7f6a960df17552e52c6d4864aa13eaa81444 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:32:20 -0700 Subject: [PATCH 2/3] CLDSRV-909: Honor bypassMaxPutObjectSize flag on CopyObject size limit --- lib/api/objectCopy.js | 2 +- tests/unit/api/objectCopy.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/api/objectCopy.js b/lib/api/objectCopy.js index 0993daf985..66fcdac833 100644 --- a/lib/api/objectCopy.js +++ b/lib/api/objectCopy.js @@ -642,7 +642,7 @@ function objectCopy(authInfo, request, sourceBucket, sourceObject, sourceVersion return next(err, destBucketMD); } const sourceSize = parseInt(sourceObjMD['content-length'], 10); - if (sourceSize > constants.maximumAllowedUploadSize) { + if (sourceSize > constants.maximumAllowedUploadSize && !config.bypassMaxPutObjectSize) { log.debug('copy source object too large', { sourceSize }); const err = errorInstances.InvalidRequest.customizeDescription( 'The specified copy source is larger than the maximum ' + diff --git a/tests/unit/api/objectCopy.js b/tests/unit/api/objectCopy.js index d12f6f9415..feafef73c6 100644 --- a/tests/unit/api/objectCopy.js +++ b/tests/unit/api/objectCopy.js @@ -1887,6 +1887,7 @@ describe('objectCopy source size limit', () => { after(() => { constants.maximumAllowedUploadSize = originalMaximumUploadSize; + config.bypassMaxPutObjectSize = false; cleanup(); }); @@ -1912,4 +1913,15 @@ describe('objectCopy source size limit', () => { done(); }); }); + + it('should allow CopyObject when source size exceeds the limit but bypass flag is set', done => { + constants.maximumAllowedUploadSize = sourceSize - 1; + config.bypassMaxPutObjectSize = true; + const testObjectCopyRequest = _createObjectCopyRequest(destBucketName); + objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, + undefined, log, err => { + assert.ifError(err); + done(); + }); + }); }); From c8e63e7402bcfbefed5d6b460fc2a10c298ea331 Mon Sep 17 00:00:00 2001 From: Thomas Carmet <8408330+tcarmet@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:34:55 -0700 Subject: [PATCH 3/3] CLDSRV-909: Use plain if for sourceServerAccessLog error assignment --- lib/api/objectCopy.js | 6 ++++-- tests/unit/api/objectCopy.js | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/api/objectCopy.js b/lib/api/objectCopy.js index 66fcdac833..13b9628cf8 100644 --- a/lib/api/objectCopy.js +++ b/lib/api/objectCopy.js @@ -648,8 +648,10 @@ function objectCopy(authInfo, request, sourceBucket, sourceObject, sourceVersion 'The specified copy source is larger than the maximum ' + `allowable size for a copy source: ${constants.maximumAllowedUploadSize}`, ); - // eslint-disable-next-line no-param-reassign - request.sourceServerAccessLog && (request.sourceServerAccessLog.error = err); + if (request.sourceServerAccessLog) { + // eslint-disable-next-line no-param-reassign + request.sourceServerAccessLog.error = err; + } return next(err, destBucketMD); } const headerValResult = validateHeaders( diff --git a/tests/unit/api/objectCopy.js b/tests/unit/api/objectCopy.js index feafef73c6..5af59326ab 100644 --- a/tests/unit/api/objectCopy.js +++ b/tests/unit/api/objectCopy.js @@ -1918,10 +1918,9 @@ describe('objectCopy source size limit', () => { constants.maximumAllowedUploadSize = sourceSize - 1; config.bypassMaxPutObjectSize = true; const testObjectCopyRequest = _createObjectCopyRequest(destBucketName); - objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, - undefined, log, err => { - assert.ifError(err); - done(); - }); + objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, undefined, log, err => { + assert.ifError(err); + done(); + }); }); });