Skip to content

Commit

Permalink
Dev/2.14.0 (#208)
Browse files Browse the repository at this point in the history
 feat: getObject 新增 Key 格式化
  • Loading branch information
livehigh authored May 24, 2024
1 parent 6938488 commit 5717876
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 6 deletions.
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,14 @@ declare namespace COS {
/** 是否开启长链接,默认开启 */
KeepAlive?: boolean;
Ip?: string;
/** 是否严格校验 HTTPS 证书,默认 true */
StrictSsl?: boolean;
/** 自定义 UserAgent */
UserAgent?: string;
/** 默认将host加入签名计算,关闭后可能导致越权风险,建议保持为true */
ForceSignHost?: boolean;
AutoSwitchHost?: boolean;
ObjectKeySimplifyCheck?: boolean;
/** 自定义拷贝源解析器 */
CopySourceParser?: null | CopySourceParserFunction;
/** 获取签名的回调方法,如果没有 SecretId、SecretKey 时,必选 */
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cos-nodejs-sdk-v5",
"version": "2.13.5",
"version": "2.14.0",
"description": "cos nodejs sdk v5",
"main": "index.js",
"types": "index.d.ts",
Expand Down
12 changes: 10 additions & 2 deletions sdk/advance.js
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,14 @@ function copySliceItem(params, callback) {

// 分片下载文件
function downloadFile(params, callback) {
if (this.options.ObjectKeySimplifyCheck) {
// getObject 的 Key 需要格式化,避免调用成 getBucket
var formatKey = util.simplifyPath(params.Key);
if (formatKey === '/') {
callback(util.error(new Error('The Getobject Key is illegal')));
return;
}
}
var self = this;
var TaskId = params.TaskId || util.uuid();
var Bucket = params.Bucket;
Expand Down Expand Up @@ -1434,8 +1442,8 @@ function downloadFile(params, callback) {
if (err) return ep.emit('error', err);

// 获取文件大小
FileSize = params.FileSize = parseInt(data.headers['content-length']);
if (FileSize === undefined || !FileSize) {
FileSize = params.FileSize = data.headers['content-length'] ? parseInt(data.headers['content-length']) : undefined;
if (FileSize === undefined) {
callback(
util.error(
new Error(
Expand Down
8 changes: 8 additions & 0 deletions sdk/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,14 @@ function listObjectVersions(params, callback) {
* @param {Object} data 为对应的 object 数据,包括 body 和 headers
*/
function getObject(params, callback) {
if (this.options.ObjectKeySimplifyCheck) {
// getObject 的 Key 需要格式化,避免调用成 getBucket
var formatKey = util.simplifyPath(params.Key);
if (formatKey === '/') {
callback(util.error(new Error('The Getobject Key is illegal')));
return;
}
}
var reqParams = params.Query || {};
var reqParamsStr = params.QueryString || '';

Expand Down
1 change: 1 addition & 0 deletions sdk/cos.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var defaultOptions = {
ForceSignHost: true, // 默认将host加入签名计算,关闭后可能导致越权风险,建议保持为true
AutoSwitchHost: true,
CopySourceParser: null, // 自定义拷贝源解析器
ObjectKeySimplifyCheck: true, // 开启合并校验 getObject Key
// 动态秘钥,优先级Credentials > SecretId/SecretKey。注意Cred内是小写的secretId、secretKey
Credentials: {
secretId: '',
Expand Down
21 changes: 19 additions & 2 deletions sdk/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,9 @@ var apiWrapper = function (apiName, apiFn) {
delete params.AppId;
}
}
// 如果 Key 是 / 开头,强制去掉第一个 /
if (!self.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/') {
// 除了下列 api,如果 Key 是 / 开头,强制去掉第一个 /
var dontRemoveApi = ['sliceUploadFile', 'uploadFile', 'downloadFile', 'sliceCopyFile'];
if (!self.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/' && !dontRemoveApi.includes(apiName)) {
params.Key = params.Key.substr(1);
}
}
Expand Down Expand Up @@ -736,6 +737,21 @@ var encodeBase64 = function (str, safe) {
return base64Str;
};

var simplifyPath = function (path) {
const names = path.split('/');
const stack = [];
for (const name of names) {
if (name === '..') {
if (stack.length) {
stack.pop();
}
} else if (name.length && name !== '.') {
stack.push(name);
}
}
return '/' + stack.join('/');
};

var util = {
noop: noop,
formatParams: formatParams,
Expand Down Expand Up @@ -772,6 +788,7 @@ var util = {
isCIHost: isCIHost,
getSourceParams: getSourceParams,
encodeBase64: encodeBase64,
simplifyPath: simplifyPath,
};

module.exports = util;
198 changes: 197 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6069,6 +6069,197 @@ group('sliceUploadFile() 续传', function () {
});
});


group('getObject() 默认开启合并 Key 校验', function () {
function getObjectErrorKey(Key, done) {
cos.getObject(
{
Bucket: config.Bucket,
Region: config.Region,
Key,
},
function (err, data) {
assert.ok(err.message === 'The Getobject Key is illegal');
done();
}
);
}
test('getObject() object The Getobject Key is illegal 1', function (done) {
getObjectErrorKey('///////', done);
});
test('getObject() object The Getobject Key is illegal 2', function (done) {
getObjectErrorKey('/abc/../', done);
});
test('getObject() object The Getobject Key is illegal 3', function (done) {
getObjectErrorKey('/./', done);
});
test('getObject() object The Getobject Key is illegal 4', function (done) {
getObjectErrorKey('///abc/.//def//../../', done);
});
test('getObject() object The Getobject Key is illegal 5', function (done) {
getObjectErrorKey('/././///abc/.//def//../../', done);
});
});

group('getObject() 手动关闭合并 Key 校验', function () {
var cos = new COS({
// 必选参数
SecretId: config.SecretId,
SecretKey: config.SecretKey,
Protocol: 'http',
ObjectKeySimplifyCheck: false,
});
function putObjectAndGetObject(Key, done) {
const Body = Key;
cos.putObject({
Bucket: config.Bucket,
Region: config.Region,
Key,
Body,
}, function(err, data) {
if (err) {
done(err);
} else {
cos.getObject(
{
Bucket: config.Bucket,
Region: config.Region,
Key,
},
function (err, data) {
assert.ok(data.Body.toString() === Body);
done();
}
);
}
});
}
test('getObject() object The Getobject Key is illegal 1', function (done) {
// 会转成 getBucket
cos.getObject(
{
Bucket: config.Bucket,
Region: config.Region,
Key: '///////',
},
function (err, data) {
assert.ok(data.Body.toString().includes('ListBucketResult'));
done();
}
);
});
test('getObject() object The Getobject Key is illegal 2', function (done) {
putObjectAndGetObject('/abc/../', done);
});
test('getObject() object The Getobject Key is illegal 3', function (done) {
// 会转成 getBucket
cos.getObject(
{
Bucket: config.Bucket,
Region: config.Region,
Key: '/./',
},
function (err, data) {
assert.ok(data.Body.toString().includes('ListBucketResult'));
done();
}
);
});
test('getObject() object The Getobject Key is illegal 4', function (done) {
putObjectAndGetObject('///abc/.//def//../../', done);
});
test('getObject() object The Getobject Key is illegal 5', function (done) {
putObjectAndGetObject('/././///abc/.//def//../../', done);
});
});

group('downloadFile() 默认开启合并 Key 校验', function () {
function downloadFileErrorKey(Key, done) {
cos.downloadFile(
{
Bucket: config.Bucket,
Region: config.Region,
Key,
FilePath: './' + Key
},
function (err, data) {
assert.ok(err.message === 'The Getobject Key is illegal');
done();
}
);
}
test('downloadFile() object The Getobject Key is illegal 1', function (done) {
downloadFileErrorKey('///////', done);
});
test('downloadFile() object The Getobject Key is illegal 2', function (done) {
downloadFileErrorKey('/abc/../', done);
});
test('downloadFile() object The Getobject Key is illegal 3', function (done) {
downloadFileErrorKey('/./', done);
});
test('downloadFile() object The Getobject Key is illegal 4', function (done) {
downloadFileErrorKey('///abc/.//def//../../', done);
});
test('downloadFile() object The Getobject Key is illegal 5', function (done) {
downloadFileErrorKey('/././///abc/.//def//../../', done);
});
});

group('downloadFile() 手动关闭合并 Key 校验', function () {
var cos = new COS({
// 必选参数
SecretId: config.SecretId,
SecretKey: config.SecretKey,
Protocol: 'http',
ObjectKeySimplifyCheck: false,
});
function getObjectOrGetBucket(Key, hasEtag, done) {
const Body = Key;
cos.putObject({
Bucket: config.Bucket,
Region: config.Region,
Key,
Body,
}, function(err, data) {
if (err) {
done();
} else {
cos.downloadFile(
{
Bucket: config.Bucket,
Region: config.Region,
Key,
FilePath: './testFile'
},
function (err, data) {
const isGetBucket = data.ETag === '';
const isGetObject = data.ETag !== '';
const ok = hasEtag ? isGetObject : isGetBucket;
fs.unlinkSync('./testFile');
assert.ok(ok);
done();
}
);
}
});
}
test('downloadFile() object The Getobject Key is illegal 1', function (done) {
getObjectOrGetBucket('///////', false, done);
});
test('downloadFile() object The Getobject Key is illegal 2', function (done) {
getObjectOrGetBucket('/abc/../', true, done);
});
test('downloadFile() object The Getobject Key is illegal 3', function (done) {
getObjectOrGetBucket('/./', false, done);
});
test('downloadFile() object The Getobject Key is illegal 4', function (done) {
getObjectOrGetBucket('///abc/.//def//../../', true, done);
});
test('downloadFile() object The Getobject Key is illegal 5', function (done) {
getObjectOrGetBucket('/././///abc/.//def//../../', true, done);
});
});

group('getStream() 流式下载 ECONNREFUSED 错误', function () {
test('getStream() 流式下载 ECONNREFUSED 错误', function (done, assert) {
cos.options.Domain = '127.0.0.1:12345';
Expand Down Expand Up @@ -6155,6 +6346,7 @@ group('downloadFile', function () {
});
test('downloadFile() fileSize=0', function (done, assert) {
var Key = '0b.zip';
var FilePath = './' + Key;
cos.downloadFile(
{
Bucket: config.Bucket, // Bucket 格式:test-1250000000
Expand All @@ -6167,7 +6359,8 @@ group('downloadFile', function () {
TaskId: '123', // 可以自己生成TaskId,用于取消下载
},
function (err, data) {
assert.ok(err);
fs.unlinkSync(FilePath);
assert.ok(!err);
done();
}
);
Expand Down Expand Up @@ -6198,6 +6391,7 @@ group('downloadFile', function () {
TaskId: '123', // 可以自己生成TaskId,用于取消下载
},
function (err, data) {
fs.unlinkSync(filePath);
assert.ok(!err);
done();
}
Expand Down Expand Up @@ -6238,6 +6432,7 @@ group('downloadFile', function () {
TaskId: '123', // 可以自己生成TaskId,用于取消下载
},
function (err, data) {
fs.unlinkSync(filePath);
assert.ok(!err);
done();
}
Expand Down Expand Up @@ -6345,6 +6540,7 @@ group('downloadFile', function () {
onProgress: function (progressData) {
if (progressData.percent >= 0.1) {
cos.emit('inner-kill-task', { TaskId: 'downloadFile-123' });
fs.unlinkSync(filePath);
assert.ok(1);
done();
}
Expand Down

0 comments on commit 5717876

Please sign in to comment.