Skip to content

Commit 850e511

Browse files
committed
Merge remote-tracking branch 'origin/bugfix/CLDSRV-729-remove-md5-header-requirement' into w/8.8/bugfix/CLDSRV-729-remove-md5-header-requirement
2 parents 5f86015 + 8ed743d commit 850e511

File tree

4 files changed

+124
-15
lines changed

4 files changed

+124
-15
lines changed

lib/api/bucketPutCors.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ function bucketPutCors(authInfo, request, log, callback) {
3333
return callback(errors.MissingRequestBodyError);
3434
}
3535

36-
const md5 = crypto.createHash('md5')
37-
.update(request.post, 'utf8').digest('base64');
38-
if (md5 !== request.headers['content-md5']) {
39-
log.debug('bad md5 digest', { error: errors.BadDigest });
40-
monitoring.promMetrics('PUT', bucketName, 400, 'putBucketCors');
41-
return callback(errors.BadDigest);
36+
if (request.headers['content-md5']) {
37+
const md5 = crypto.createHash('md5')
38+
.update(request.post, 'utf8').digest('base64');
39+
if (md5 !== request.headers['content-md5']) {
40+
log.debug('bad md5 digest', { error: errors.BadDigest });
41+
monitoring.promMetrics('PUT', bucketName, 400, 'putBucketCors');
42+
return callback(errors.BadDigest);
43+
}
4244
}
4345

4446
if (parseInt(request.headers['content-length'], 10) > 65536) {

lib/api/multiObjectDelete.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,12 +476,15 @@ function multiObjectDelete(authInfo, request, log, callback) {
476476
'multiObjectDelete');
477477
return callback(errors.MissingRequestBodyError);
478478
}
479-
const md5 = crypto.createHash('md5')
480-
.update(request.post, 'utf8').digest('base64');
481-
if (md5 !== request.headers['content-md5']) {
482-
monitoring.promMetrics('DELETE', request.bucketName, 400,
483-
'multiObjectDelete');
484-
return callback(errors.BadDigest);
479+
480+
if (request.headers['content-md5']) {
481+
const md5 = crypto.createHash('md5')
482+
.update(request.post, 'utf8').digest('base64');
483+
if (md5 !== request.headers['content-md5']) {
484+
monitoring.promMetrics('DELETE', request.bucketName, 400,
485+
'multiObjectDelete');
486+
return callback(errors.BadDigest);
487+
}
485488
}
486489

487490
const inPlayInternal = [];

tests/unit/api/bucketPutCors.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('putBucketCORS API', () => {
5555
.createBucketCorsRequest('PUT', bucketName);
5656
bucketPutCors(authInfo, testBucketPutCorsRequest, log, err => {
5757
if (err) {
58-
process.stdout.write(`Err putting website config ${err}`);
58+
process.stdout.write(`Err putting bucket cors ${err}`);
5959
return done(err);
6060
}
6161
return metadata.getBucket(bucketName, log, (err, bucket) => {
@@ -70,11 +70,33 @@ describe('putBucketCORS API', () => {
7070
});
7171
});
7272

73-
it('should return BadDigest if md5 is omitted', done => {
73+
it('should accept request if md5 is omitted', done => {
7474
const corsUtil = new CorsConfigTester();
7575
const testBucketPutCorsRequest = corsUtil
7676
.createBucketCorsRequest('PUT', bucketName);
7777
testBucketPutCorsRequest.headers['content-md5'] = undefined;
78+
bucketPutCors(authInfo, testBucketPutCorsRequest, log, err => {
79+
if (err) {
80+
process.stdout.write(`Err putting bucket cors ${err}`);
81+
return done(err);
82+
}
83+
return metadata.getBucket(bucketName, log, (err, bucket) => {
84+
if (err) {
85+
process.stdout.write(`Err retrieving bucket MD ${err}`);
86+
return done(err);
87+
}
88+
const uploadedCors = bucket.getCors();
89+
assert.deepStrictEqual(uploadedCors, corsUtil.getCors());
90+
return done();
91+
});
92+
});
93+
});
94+
95+
it('should reject request if md5 is mismatch', done => {
96+
const corsUtil = new CorsConfigTester();
97+
const testBucketPutCorsRequest = corsUtil
98+
.createBucketCorsRequest('PUT', bucketName);
99+
testBucketPutCorsRequest.headers['content-md5'] = 'wrong md5';
78100
_testPutBucketCors(authInfo, testBucketPutCorsRequest,
79101
log, 'BadDigest', done);
80102
});

tests/unit/api/multiObjectDelete.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,12 @@ describe('multiObjectDelete function', () => {
377377
objectKey: 'objectname',
378378
parsedHost: 'localhost',
379379
headers: {
380-
'content-md5': crypto.createHash('md5').update(post, 'utf8').digest('base64')
380+
'content-md5': crypto.createHash('md5').update(post, 'utf8').digest('base64'),
381381
},
382382
post,
383+
socket: {
384+
remoteAddress: '127.0.0.1',
385+
},
383386
url: '/bucketname',
384387
});
385388
const authInfo = makeAuthInfo('123456');
@@ -403,4 +406,83 @@ describe('multiObjectDelete function', () => {
403406
done();
404407
});
405408
});
409+
410+
it('should accept request when content-md5 header is missing', done => {
411+
const post = '<Delete><Object><Key>objectname</Key></Object></Delete>';
412+
const testObjectKey = 'objectname';
413+
const testBucketName = 'test-bucket';
414+
const request = new DummyRequest({
415+
bucketName: testBucketName,
416+
objectKey: testObjectKey,
417+
parsedHost: 'localhost',
418+
headers: {
419+
// No content-md5 header
420+
},
421+
post,
422+
socket: {
423+
remoteAddress: '127.0.0.1',
424+
},
425+
url: `/${testBucketName}`,
426+
});
427+
428+
// Use the same canonicalID for both authInfo and bucket owner to avoid AccessDenied
429+
const testAuthInfo = makeAuthInfo(canonicalID);
430+
431+
// Create bucket with proper ownership
432+
const testBucketRequest = new DummyRequest({
433+
bucketName: testBucketName,
434+
namespace,
435+
headers: {},
436+
url: `/${testBucketName}`,
437+
});
438+
439+
// Create object to delete
440+
const testObjectRequest = new DummyRequest({
441+
bucketName: testBucketName,
442+
namespace,
443+
objectKey: testObjectKey,
444+
headers: {},
445+
url: `/${testBucketName}/${testObjectKey}`,
446+
}, postBody);
447+
448+
bucketPut(testAuthInfo, testBucketRequest, log, () => {
449+
objectPut(testAuthInfo, testObjectRequest, undefined, log, () => {
450+
multiObjectDelete.multiObjectDelete(testAuthInfo, request, log, (err, res) => {
451+
// Request should succeed even without content-md5 header
452+
assert.strictEqual(err, null);
453+
assert.strictEqual(typeof res, 'string');
454+
// Should contain successful deletion response
455+
assert.strictEqual(res.includes('<Deleted><Key>objectname</Key></Deleted>'), true);
456+
done();
457+
});
458+
});
459+
});
460+
});
461+
462+
it('should reject request with BadDigest error when content-md5 header mismatches', done => {
463+
const post = '<Delete><Object><Key>objectname</Key></Object></Delete>';
464+
const incorrectMd5 = 'incorrectMd5Hash';
465+
466+
const request = new DummyRequest({
467+
bucketName: 'bucketname',
468+
objectKey: 'objectname',
469+
parsedHost: 'localhost',
470+
headers: {
471+
'content-md5': incorrectMd5,
472+
},
473+
post,
474+
socket: {
475+
remoteAddress: '127.0.0.1',
476+
},
477+
url: '/bucketname',
478+
});
479+
const authInfo = makeAuthInfo('123456');
480+
481+
multiObjectDelete.multiObjectDelete(authInfo, request, log, (err, res) => {
482+
// Should return BadDigest error for mismatched content-md5
483+
assert.strictEqual(err.is.BadDigest, true);
484+
assert.strictEqual(res, undefined);
485+
done();
486+
});
487+
});
406488
});

0 commit comments

Comments
 (0)