Skip to content

Commit 3c53f3e

Browse files
authored
Dev/2.13.0 (#197)
1、安全性提升:不再支持 path-style 2、安全性提升:host 强制加入签名计算 3、修复 uploadFile 未对参数 Key 做校验
1 parent c38c9a5 commit 3c53f3e

File tree

7 files changed

+107
-23
lines changed

7 files changed

+107
-23
lines changed

demo/demo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var cos = new COS({
1717
ChunkSize: 1024 * 1024 * 8, // 控制分片大小,单位 B,在同园区上传可以设置较大的分片大小
1818
Proxy: '',
1919
Protocol: 'https:',
20-
FollowRedirect: false,
20+
Timeout: 10000,
2121
});
2222

2323
var TaskId;

index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ declare namespace COS {
198198
Ip?: string;
199199
/** 默认将host加入签名计算,关闭后可能导致越权风险,建议保持为true */
200200
ForceSignHost?: boolean;
201+
AutoSwitchHost?: boolean;
201202
/** 获取签名的回调方法,如果没有 SecretId、SecretKey 时,必选 */
202203
getAuthorization?: (
203204
options: GetAuthorizationOptions,
@@ -311,6 +312,10 @@ declare namespace COS {
311312
message: string;
312313
/** 兼容老的错误信息字段,不建议使用,可能是参数错误、客户端出错、或服务端返回的错误 */
313314
error: string | Error | { Code: string; Message: string };
315+
/** 当前请求的Url */
316+
url: string;
317+
/** 当前请求的method */
318+
method: string;
314319
}
315320
/** 回调的错误格式,其中服务端返回错误码可查看 @see https://cloud.tencent.com/document/product/436/7730 */
316321
type CosError = null | CosSdkError;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cos-nodejs-sdk-v5",
3-
"version": "2.12.6",
3+
"version": "2.13.0",
44
"description": "cos nodejs sdk v5",
55
"main": "index.js",
66
"types": "index.d.ts",

sdk/advance.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ function sliceUploadFile(params, callback) {
2525
// 上传过程中出现错误,返回错误
2626
ep.on('error', function (err) {
2727
if (!self._isRunningTask(TaskId)) return;
28-
var _err = {
29-
UploadId: params.UploadData.UploadId || '',
30-
err: err,
31-
};
28+
err.UploadId = params.UploadData.UploadId || '';
3229
return callback(_err);
3330
});
3431

@@ -652,6 +649,7 @@ function uploadSliceItem(params, callback) {
652649
util.getFileMd5(md5Body, function (err, md5) {
653650
var contentMd5 = md5 ? util.binaryBase64(md5) : '';
654651
var PartItem = UploadData.PartList[PartNumber - 1];
652+
var switchHost = false;
655653
Async.retry(
656654
ChunkRetryTimes,
657655
function (tryCallback) {
@@ -671,9 +669,13 @@ function uploadSliceItem(params, callback) {
671669
Headers: headers,
672670
onProgress: params.onProgress,
673671
ContentMD5: contentMd5,
672+
SwitchHost: switchHost,
674673
},
675674
function (err, data) {
676675
if (!self._isRunningTask(TaskId)) return;
676+
if (err) {
677+
switchHost = err?.switchHost;
678+
}
677679
if (err) return tryCallback(err);
678680
PartItem.Uploaded = true;
679681
return tryCallback(null, data);
@@ -682,6 +684,9 @@ function uploadSliceItem(params, callback) {
682684
});
683685
},
684686
function (err, data) {
687+
if (err) {
688+
delete err.switchHost;
689+
}
685690
if (!self._isRunningTask(TaskId)) return;
686691
return callback(err, data);
687692
}

sdk/base.js

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3020,6 +3020,7 @@ function multipartUpload(params, callback) {
30203020
headers: params.Headers,
30213021
onProgress: params.onProgress,
30223022
body: params.Body || null,
3023+
SwitchHost: params.SwitchHost,
30233024
},
30243025
function (err, data) {
30253026
if (err) return callback(err);
@@ -3644,9 +3645,7 @@ var getSignHost = function (opt) {
36443645
region: useAccelerate ? 'accelerate' : opt.Region,
36453646
});
36463647
var urlHost = url.replace(/^https?:\/\/([^/]+)(\/.*)?$/, '$1');
3647-
var standardHostReg = new RegExp('^([a-z\\d-]+-\\d+\\.)?(cos|cosv6|ci|pic)\\.([a-z\\d-]+)\\.myqcloud\\.com$');
3648-
if (standardHostReg.test(urlHost)) return urlHost;
3649-
return '';
3648+
return urlHost;
36503649
};
36513650

36523651
// 异步获取签名
@@ -3747,6 +3746,7 @@ function getAuthorizationAsync(params, callback) {
37473746
Token: StsData.Token || '',
37483747
ClientIP: StsData.ClientIP || '',
37493748
ClientUA: StsData.ClientUA || '',
3749+
SignFrom: 'client',
37503750
};
37513751
cb(null, AuthData);
37523752
};
@@ -3870,6 +3870,7 @@ function getAuthorizationAsync(params, callback) {
38703870
var AuthData = {
38713871
Authorization: Authorization,
38723872
SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken,
3873+
SignFrom: 'client',
38733874
};
38743875
cb(null, AuthData);
38753876
return AuthData;
@@ -3878,9 +3879,11 @@ function getAuthorizationAsync(params, callback) {
38783879
return '';
38793880
}
38803881

3881-
// 调整时间偏差
3882+
// 判断当前请求出错时能否重试
38823883
function allowRetry(err) {
3883-
var allowRetry = false;
3884+
var self = this;
3885+
var canRetry = false;
3886+
var networkError = false;
38843887
var isTimeError = false;
38853888
var serverDate = (err.headers && (err.headers.date || err.headers.Date)) || (err.error && err.error.ServerTime);
38863889
try {
@@ -3894,6 +3897,7 @@ function allowRetry(err) {
38943897
}
38953898
} catch (e) {}
38963899
if (err) {
3900+
// 调整时间偏差
38973901
if (isTimeError && serverDate) {
38983902
var serverTime = Date.parse(serverDate);
38993903
if (
@@ -3902,15 +3906,48 @@ function allowRetry(err) {
39023906
) {
39033907
console.error('error: Local time is too skewed.');
39043908
this.options.SystemClockOffset = serverTime - Date.now();
3905-
allowRetry = true;
3909+
canRetry = true;
39063910
}
39073911
} else if (Math.floor(err.statusCode / 100) === 5) {
3908-
allowRetry = true;
3912+
canRetry = true;
39093913
} else if (err.code === 'ECONNRESET') {
3910-
allowRetry = true;
3914+
canRetry = true;
3915+
}
3916+
/**
3917+
* 归为网络错误
3918+
* 1、no statusCode
3919+
* 2、statusCode === 3xx || 4xx || 5xx && no requestId
3920+
*/
3921+
if (!err.statusCode) {
3922+
canRetry = self.options.AutoSwitchHost;
3923+
networkError = true;
3924+
} else {
3925+
const statusCode = Math.floor(err.statusCode / 100);
3926+
const requestId = err?.headers && err?.headers['x-cos-request-id'];
3927+
if ([3, 4, 5].includes(statusCode) && !requestId) {
3928+
canRetry = self.options.AutoSwitchHost;
3929+
networkError = true;
3930+
}
39113931
}
39123932
}
3913-
return allowRetry;
3933+
return { canRetry, networkError };
3934+
}
3935+
3936+
/**
3937+
* requestUrl:请求的url,用于判断是否cos主域名,true才切
3938+
* clientCalcSign:是否客户端计算签名,服务端返回的签名不能切,true才切
3939+
* networkError:是否未知网络错误,true才切
3940+
* */
3941+
function canSwitchHost({ requestUrl, clientCalcSign, networkError }) {
3942+
if (!this.options.AutoSwitchHost) return false;
3943+
if (!requestUrl) return false;
3944+
if (!clientCalcSign) return false;
3945+
if (!networkError) return false;
3946+
const commonReg = /^https?:\/\/[^\/]*\.cos\.[^\/]*\.myqcloud\.com(\/.*)?$/;
3947+
const accelerateReg = /^https?:\/\/[^\/]*\.cos\.accelerate\.myqcloud\.com(\/.*)?$/;
3948+
// 当前域名是cos主域名才切换
3949+
const isCommonCosHost = commonReg.test(requestUrl) && !accelerateReg.test(requestUrl);
3950+
return isCommonCosHost;
39143951
}
39153952

39163953
// 获取签名并发起请求
@@ -3937,6 +3974,11 @@ function submitRequest(params, callback) {
39373974
params.SignHost || getSignHost.call(this, { Bucket: params.Bucket, Region: params.Region, Url: params.url });
39383975
var next = function (tryTimes) {
39393976
var oldClockOffset = self.options.SystemClockOffset;
3977+
if (params.SwitchHost) {
3978+
// 更换要签的host
3979+
SignHost = SignHost.replace(/myqcloud.com/, 'tencentcos.cn');
3980+
}
3981+
39403982
getAuthorizationAsync.call(
39413983
self,
39423984
{
@@ -3951,18 +3993,20 @@ function submitRequest(params, callback) {
39513993
ResourceKey: params.ResourceKey,
39523994
Scope: params.Scope,
39533995
ForceSignHost: self.options.ForceSignHost,
3996+
SwitchHost: params.SwitchHost,
39543997
},
39553998
function (err, AuthData) {
39563999
if (err) return callback(err);
39574000
params.AuthData = AuthData;
39584001
_submitRequest.call(self, params, function (err, data) {
3959-
if (
3960-
err &&
3961-
!(params.body && params.body.pipe) &&
3962-
!params.outputStream &&
3963-
tryTimes < 2 &&
3964-
(oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))
3965-
) {
4002+
let canRetry = false;
4003+
let networkError = false;
4004+
if (err) {
4005+
const info = allowRetry.call(self, err);
4006+
canRetry = info.canRetry || oldClockOffset !== self.options.SystemClockOffset;
4007+
networkError = info.networkError;
4008+
}
4009+
if (err && !(params.body && params.body.pipe) && !params.outputStream && tryTimes < 2 && canRetry) {
39664010
if (params.headers) {
39674011
delete params.headers.Authorization;
39684012
delete params.headers['token'];
@@ -3971,8 +4015,23 @@ function submitRequest(params, callback) {
39714015
params.headers['x-cos-security-token'] && delete params.headers['x-cos-security-token'];
39724016
params.headers['x-ci-security-token'] && delete params.headers['x-ci-security-token'];
39734017
}
4018+
// 进入重试逻辑时 需判断是否需要切换cos备用域名
4019+
const switchHost = canSwitchHost.call(self, {
4020+
requestUrl: err?.url || '',
4021+
clientCalcSign: AuthData?.SignFrom === 'client',
4022+
networkError,
4023+
});
4024+
params.SwitchHost = switchHost;
39744025
next(tryTimes + 1);
39754026
} else {
4027+
if (err && params.Action === 'name/cos:UploadPart') {
4028+
const switchHost = canSwitchHost.call(self, {
4029+
requestUrl: err?.url || '',
4030+
clientCalcSign: AuthData?.SignFrom === 'client',
4031+
networkError,
4032+
});
4033+
err.switchHost = switchHost;
4034+
}
39764035
callback(err, data);
39774036
}
39784037
});
@@ -4018,6 +4077,10 @@ function _submitRequest(params, callback) {
40184077
region: region,
40194078
object: object,
40204079
});
4080+
if (params.SwitchHost) {
4081+
// 更换请求的url
4082+
url = url.replace(/myqcloud.com/, 'tencentcos.cn');
4083+
}
40214084
if (params.action) {
40224085
url = url + '?' + params.action;
40234086
}
@@ -4113,6 +4176,8 @@ function _submitRequest(params, callback) {
41134176
retResponse && retResponse.statusCode && (attrs.statusCode = retResponse.statusCode);
41144177
retResponse && retResponse.headers && (attrs.headers = retResponse.headers);
41154178
if (err) {
4179+
opt.url && (attrs.url = opt.url);
4180+
opt.method && (attrs.method = opt.method);
41164181
err = util.extend(err || {}, attrs);
41174182
callback(err, null);
41184183
} else {

sdk/cos.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ var defaultOptions = {
4444
UserAgent: '',
4545
ConfCwd: '',
4646
ForceSignHost: true, // 默认将host加入签名计算,关闭后可能导致越权风险,建议保持为true
47+
AutoSwitchHost: true,
4748
// 动态秘钥,优先级Credentials > SecretId/SecretKey。注意Cred内是小写的secretId、secretKey
4849
Credentials: {
4950
secretId: '',
@@ -101,13 +102,20 @@ var COS = function (options) {
101102
console.error('error: SecretKey format is incorrect. Please check');
102103
}
103104
if (util.isWeb()) {
105+
console.log('Tip: 使用 electron 等跨平台技术可正常使用Nodejs SDK,请忽略下方浏览器环境警告');
104106
console.warn(
105107
'warning: cos-nodejs-sdk-v5 不支持浏览器使用,请改用 cos-js-sdk-v5,参考文档: https://cloud.tencent.com/document/product/436/11459'
106108
);
107109
console.warn(
108110
'warning: cos-nodejs-sdk-v5 does not support browsers. Please use cos-js-sdk-v5 instead, See: https://cloud.tencent.com/document/product/436/11459'
109111
);
110112
}
113+
if (this.options.ForcePathStyle) {
114+
console.warn(
115+
'cos-nodejs-sdk-v5不再支持使用path-style,仅支持使用virtual-hosted-style,参考文档:https://cloud.tencent.com/document/product/436/96243'
116+
);
117+
throw new Error('ForcePathStyle is not supported');
118+
}
111119
event.init(this);
112120
task.init(this);
113121

sdk/util.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ var hasMissingParams = function (apiName, params) {
375375
apiName.indexOf('Object') > -1 ||
376376
apiName.indexOf('multipart') > -1 ||
377377
apiName === 'sliceUploadFile' ||
378-
apiName === 'abortUploadTask'
378+
apiName === 'abortUploadTask' ||
379+
apiName === 'uploadFile'
379380
) {
380381
if (!Bucket) return 'Bucket';
381382
if (!Region) return 'Region';

0 commit comments

Comments
 (0)