@nest-omni/core 4.1.3-25 → 4.1.3-27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cache/cache.service.d.ts +3 -1
- package/cache/cache.service.js +8 -8
- package/cache/decorators/cache-put.decorator.js +5 -4
- package/cache/dependencies/callback.dependency.js +9 -0
- package/cache/dependencies/tag.dependency.d.ts +1 -9
- package/cache/dependencies/tag.dependency.js +5 -14
- package/cache/providers/lrucache.provider.d.ts +1 -0
- package/cache/providers/lrucache.provider.js +6 -4
- package/cache/providers/redis-cache.provider.d.ts +1 -0
- package/cache/providers/redis-cache.provider.js +8 -6
- package/http-client/config/http-client.config.js +4 -2
- package/http-client/decorators/http-client.decorators.d.ts +1 -1
- package/http-client/decorators/http-client.decorators.js +1 -1
- package/http-client/examples/advanced-usage.example.js +3 -3
- package/http-client/examples/axios-config-extended.example.js +1 -3
- package/http-client/examples/flexible-response-example.d.ts +28 -0
- package/http-client/examples/flexible-response-example.js +120 -0
- package/http-client/examples/ssl-certificate.example.d.ts +2 -2
- package/http-client/examples/ssl-certificate.example.js +18 -17
- package/http-client/http-client.module.js +2 -2
- package/http-client/interfaces/http-client-config.interface.d.ts +6 -1
- package/http-client/services/api-client-registry.service.d.ts +6 -6
- package/http-client/services/api-client-registry.service.js +9 -9
- package/http-client/services/circuit-breaker.service.d.ts +9 -9
- package/http-client/services/circuit-breaker.service.js +24 -24
- package/http-client/services/http-client.service.d.ts +30 -13
- package/http-client/services/http-client.service.js +76 -47
- package/http-client/services/logging.service.d.ts +17 -33
- package/http-client/services/logging.service.js +81 -169
- package/http-client/utils/curl-generator.util.js +2 -5
- package/http-client/utils/index.d.ts +1 -0
- package/http-client/utils/index.js +1 -0
- package/http-client/utils/proxy-environment.util.d.ts +12 -12
- package/http-client/utils/proxy-environment.util.js +25 -19
- package/http-client/utils/retry-recorder.util.d.ts +0 -4
- package/http-client/utils/retry-recorder.util.js +2 -27
- package/http-client/utils/sanitize.util.d.ts +58 -0
- package/http-client/utils/sanitize.util.js +188 -0
- package/http-client/utils/security-validator.util.d.ts +19 -19
- package/http-client/utils/security-validator.util.js +66 -64
- package/interceptors/http-logging-interceptor.service.d.ts +38 -0
- package/interceptors/http-logging-interceptor.service.js +167 -0
- package/interceptors/index.d.ts +1 -0
- package/interceptors/index.js +1 -0
- package/package.json +1 -1
- package/setup/bootstrap.setup.js +1 -1
- package/shared/services/api-config.service.js +3 -18
- package/vault/vault-config.service.js +1 -1
|
@@ -25,6 +25,7 @@ const http_log_entity_1 = require("../entities/http-log.entity");
|
|
|
25
25
|
const request_id_util_1 = require("../utils/request-id.util");
|
|
26
26
|
const context_extractor_util_1 = require("../utils/context-extractor.util");
|
|
27
27
|
const call_stack_extractor_util_1 = require("../utils/call-stack-extractor.util");
|
|
28
|
+
const sanitize_util_1 = require("../utils/sanitize.util");
|
|
28
29
|
const transaction_1 = require("@nest-omni/transaction");
|
|
29
30
|
/**
|
|
30
31
|
* HTTP日志服务
|
|
@@ -61,13 +62,15 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
61
62
|
}
|
|
62
63
|
initRepository(dataSource, tableName) {
|
|
63
64
|
try {
|
|
64
|
-
this.logRepository =
|
|
65
|
+
this.logRepository =
|
|
66
|
+
(0, transaction_1.getDataSource)(dataSource).getRepository(http_log_entity_1.HttpLogEntity);
|
|
65
67
|
this.logger.log('Database logging initialized');
|
|
66
68
|
return this.logRepository;
|
|
67
69
|
}
|
|
68
70
|
catch (error) {
|
|
69
|
-
this.logger.
|
|
70
|
-
|
|
71
|
+
this.logger.warn('Database logging not available, continuing without it');
|
|
72
|
+
this.logRepository = null;
|
|
73
|
+
return null;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
@@ -77,16 +80,16 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
77
80
|
// 从ContextProvider获取上下文信息
|
|
78
81
|
const context = context_extractor_util_1.ContextExtractor.getHttpContext();
|
|
79
82
|
const actualRequestId = requestId || context.requestId || (0, request_id_util_1.generateRequestId)();
|
|
80
|
-
const { logHeaders = true, logBody = true,
|
|
83
|
+
const { logHeaders = true, logBody = true, sanitize = [], } = loggingOptions || {};
|
|
81
84
|
const logData = {
|
|
82
85
|
requestId: actualRequestId,
|
|
83
86
|
userId: context.userId,
|
|
84
|
-
method: config.method ? config.method.toUpperCase() : 'GET',
|
|
85
|
-
url: config.url,
|
|
86
|
-
headers: logHeaders
|
|
87
|
-
?
|
|
87
|
+
method: (config === null || config === void 0 ? void 0 : config.method) ? config.method.toUpperCase() : 'GET',
|
|
88
|
+
url: config ? sanitize_util_1.SanitizeUtil.sanitizeQueryString(config.url, sanitize) : '',
|
|
89
|
+
headers: logHeaders && config
|
|
90
|
+
? sanitize_util_1.SanitizeUtil.sanitizeHeaders(config.headers, sanitize)
|
|
88
91
|
: undefined,
|
|
89
|
-
body: logBody ?
|
|
92
|
+
body: logBody && config ? sanitize_util_1.SanitizeUtil.sanitizeBody(config.data, sanitize) : undefined,
|
|
90
93
|
clientIp: context.clientIp,
|
|
91
94
|
serviceName: context.appId,
|
|
92
95
|
};
|
|
@@ -107,8 +110,8 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
107
110
|
*/
|
|
108
111
|
logRequestSuccess(response_1, startTime_1, requestId_1, loggingOptions_1) {
|
|
109
112
|
return __awaiter(this, arguments, void 0, function* (response, startTime, requestId, loggingOptions, databaseLogging = false, retryRecords, circuitBreakerState, decoratorContext, clientName, callingContext) {
|
|
110
|
-
var _a, _b, _c, _d;
|
|
111
|
-
const { logHeaders = true, logBody = true,
|
|
113
|
+
var _a, _b, _c, _d, _e, _f;
|
|
114
|
+
const { logHeaders = true, logBody = true, sanitize = [], } = loggingOptions || {};
|
|
112
115
|
const responseTime = Date.now() - startTime;
|
|
113
116
|
// 获取当前上下文信息
|
|
114
117
|
const context = context_extractor_util_1.ContextExtractor.getHttpContext();
|
|
@@ -118,9 +121,9 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
118
121
|
statusCode: response.status,
|
|
119
122
|
responseTime,
|
|
120
123
|
headers: logHeaders
|
|
121
|
-
?
|
|
124
|
+
? sanitize_util_1.SanitizeUtil.sanitizeHeaders(response.headers, sanitize)
|
|
122
125
|
: undefined,
|
|
123
|
-
body: logBody ?
|
|
126
|
+
body: logBody ? sanitize_util_1.SanitizeUtil.sanitizeBody(response.data, sanitize) : undefined,
|
|
124
127
|
responseSize: JSON.stringify(response.data).length,
|
|
125
128
|
circuitBreakerState,
|
|
126
129
|
};
|
|
@@ -139,7 +142,7 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
139
142
|
dataSource: (_a = loggingOptions.databaseLogging) === null || _a === void 0 ? void 0 : _a.dataSource,
|
|
140
143
|
tableName: (_b = loggingOptions.databaseLogging) === null || _b === void 0 ? void 0 : _b.tableName,
|
|
141
144
|
});
|
|
142
|
-
this.initRepository(loggingOptions.databaseLogging.dataSource, loggingOptions.databaseLogging.tableName);
|
|
145
|
+
this.initRepository(((_c = loggingOptions.databaseLogging) === null || _c === void 0 ? void 0 : _c.dataSource) || 'default', ((_d = loggingOptions.databaseLogging) === null || _d === void 0 ? void 0 : _d.tableName) || 'http_log');
|
|
143
146
|
}
|
|
144
147
|
// 数据库日志记录
|
|
145
148
|
if (databaseLogging && this.logRepository) {
|
|
@@ -156,25 +159,25 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
156
159
|
this.saveToDatabase({
|
|
157
160
|
requestId,
|
|
158
161
|
userId: context.userId,
|
|
159
|
-
method: ((
|
|
160
|
-
url: this.getFullUrl(response.config)
|
|
161
|
-
headers:
|
|
162
|
-
body:
|
|
163
|
-
params: response.config.params,
|
|
162
|
+
method: ((_e = response.config.method) === null || _e === void 0 ? void 0 : _e.toUpperCase()) || 'UNKNOWN',
|
|
163
|
+
url: sanitize_util_1.SanitizeUtil.sanitizeQueryString(this.getFullUrl(response.config), sanitize),
|
|
164
|
+
headers: sanitize_util_1.SanitizeUtil.sanitizeHeaders(response.config.headers, sanitize),
|
|
165
|
+
body: sanitize_util_1.SanitizeUtil.sanitizeBodyAsString(response.config.data, sanitize),
|
|
166
|
+
params: sanitize_util_1.SanitizeUtil.sanitizeParams(response.config.params, sanitize),
|
|
164
167
|
statusCode: response.status,
|
|
165
168
|
responseTime,
|
|
166
169
|
attemptCount: (retryRecords === null || retryRecords === void 0 ? void 0 : retryRecords.length)
|
|
167
170
|
? Math.max(...retryRecords.map((r) => r.attempt)) + 1
|
|
168
171
|
: 1,
|
|
169
172
|
success: true,
|
|
170
|
-
responseHeaders:
|
|
173
|
+
responseHeaders: sanitize_util_1.SanitizeUtil.sanitizeHeaders(response.headers, sanitize),
|
|
171
174
|
responseBody: logBody
|
|
172
|
-
?
|
|
175
|
+
? sanitize_util_1.SanitizeUtil.sanitizeBodyAsString(response.data, sanitize)
|
|
173
176
|
: undefined,
|
|
174
177
|
serviceName: clientName || context.appId,
|
|
175
178
|
operationName: callInfo.operationName,
|
|
176
179
|
clientIp: context.clientIp,
|
|
177
|
-
source: (
|
|
180
|
+
source: (_f = context.metadata) === null || _f === void 0 ? void 0 : _f.source,
|
|
178
181
|
tags: context.tags,
|
|
179
182
|
metadata: logData,
|
|
180
183
|
retryRecords,
|
|
@@ -187,8 +190,8 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
187
190
|
* 记录请求错误
|
|
188
191
|
*/
|
|
189
192
|
logRequestError(error, startTime, requestId, attemptCount, loggingOptions, databaseLogging = false, retryRecords, circuitBreakerState, decoratorContext, clientName, callingContext) {
|
|
190
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
191
|
-
const { logHeaders = true,
|
|
193
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
194
|
+
const { logHeaders = true, sanitize = [] } = loggingOptions;
|
|
192
195
|
const responseTime = Date.now() - startTime;
|
|
193
196
|
// 获取当前上下文信息
|
|
194
197
|
const context = context_extractor_util_1.ContextExtractor.getHttpContext();
|
|
@@ -202,7 +205,7 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
202
205
|
errorCode: error.code,
|
|
203
206
|
statusCode: (_a = error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
204
207
|
headers: ((_b = error.config) === null || _b === void 0 ? void 0 : _b.headers) && logHeaders
|
|
205
|
-
?
|
|
208
|
+
? sanitize_util_1.SanitizeUtil.sanitizeHeaders(error.config.headers, sanitize)
|
|
206
209
|
: undefined,
|
|
207
210
|
circuitBreakerState,
|
|
208
211
|
};
|
|
@@ -222,7 +225,7 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
222
225
|
break;
|
|
223
226
|
}
|
|
224
227
|
if (!this.logRepository) {
|
|
225
|
-
this.initRepository(loggingOptions.databaseLogging.dataSource, loggingOptions.databaseLogging.tableName);
|
|
228
|
+
this.initRepository(((_c = loggingOptions.databaseLogging) === null || _c === void 0 ? void 0 : _c.dataSource) || 'default', ((_d = loggingOptions.databaseLogging) === null || _d === void 0 ? void 0 : _d.tableName) || 'http_log');
|
|
226
229
|
}
|
|
227
230
|
// 数据库日志记录
|
|
228
231
|
if (databaseLogging && this.logRepository) {
|
|
@@ -233,14 +236,14 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
233
236
|
this.saveToDatabase({
|
|
234
237
|
requestId,
|
|
235
238
|
userId: context.userId,
|
|
236
|
-
method: ((
|
|
237
|
-
url: this.getFullUrl(error.config)
|
|
238
|
-
headers: ((
|
|
239
|
-
?
|
|
239
|
+
method: ((_f = (_e = error.config) === null || _e === void 0 ? void 0 : _e.method) === null || _f === void 0 ? void 0 : _f.toUpperCase()) || 'UNKNOWN',
|
|
240
|
+
url: sanitize_util_1.SanitizeUtil.sanitizeQueryString(this.getFullUrl(error.config), sanitize),
|
|
241
|
+
headers: ((_g = error.config) === null || _g === void 0 ? void 0 : _g.headers)
|
|
242
|
+
? sanitize_util_1.SanitizeUtil.sanitizeHeaders(error.config.headers, sanitize)
|
|
240
243
|
: {},
|
|
241
|
-
body:
|
|
242
|
-
params: (
|
|
243
|
-
statusCode: (
|
|
244
|
+
body: sanitize_util_1.SanitizeUtil.sanitizeBodyAsString((_h = error.config) === null || _h === void 0 ? void 0 : _h.data, sanitize),
|
|
245
|
+
params: sanitize_util_1.SanitizeUtil.sanitizeParams((_j = error.config) === null || _j === void 0 ? void 0 : _j.params, sanitize),
|
|
246
|
+
statusCode: (_k = error.response) === null || _k === void 0 ? void 0 : _k.status,
|
|
244
247
|
responseTime,
|
|
245
248
|
attemptCount,
|
|
246
249
|
success: false,
|
|
@@ -250,7 +253,7 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
250
253
|
serviceName: clientName || context.appId,
|
|
251
254
|
operationName: callInfo.operationName,
|
|
252
255
|
clientIp: context.clientIp,
|
|
253
|
-
source: (
|
|
256
|
+
source: (_l = context.metadata) === null || _l === void 0 ? void 0 : _l.source,
|
|
254
257
|
tags: context.tags,
|
|
255
258
|
metadata: logData,
|
|
256
259
|
retryRecords,
|
|
@@ -374,6 +377,50 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
374
377
|
};
|
|
375
378
|
});
|
|
376
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* 获取日志队列统计信息
|
|
382
|
+
*/
|
|
383
|
+
getQueueStats() {
|
|
384
|
+
return {
|
|
385
|
+
queueSize: this.logQueue.length,
|
|
386
|
+
isProcessing: this.isProcessing,
|
|
387
|
+
config: Object.assign({}, this.asyncConfig),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* 手动刷新日志队列
|
|
392
|
+
*/
|
|
393
|
+
manualFlush() {
|
|
394
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
395
|
+
return new Promise((resolve) => {
|
|
396
|
+
const checkProcessing = () => {
|
|
397
|
+
if (!this.isProcessing) {
|
|
398
|
+
this.flush();
|
|
399
|
+
// 等待刷新完成
|
|
400
|
+
setTimeout(() => resolve(), 100);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
setTimeout(checkProcessing, 50);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
checkProcessing();
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 设置异步日志配置
|
|
412
|
+
*/
|
|
413
|
+
setAsyncConfig(config) {
|
|
414
|
+
Object.assign(this.asyncConfig, config);
|
|
415
|
+
// 重启刷新任务
|
|
416
|
+
if (this.flushTimer) {
|
|
417
|
+
clearInterval(this.flushTimer);
|
|
418
|
+
}
|
|
419
|
+
if (this.asyncConfig.enabled) {
|
|
420
|
+
this.startFlushTask();
|
|
421
|
+
}
|
|
422
|
+
this.logger.log(`Async log config updated: ${JSON.stringify(this.asyncConfig)}`);
|
|
423
|
+
}
|
|
377
424
|
/**
|
|
378
425
|
* 提取调用信息
|
|
379
426
|
*/
|
|
@@ -536,141 +583,6 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
|
|
|
536
583
|
}
|
|
537
584
|
});
|
|
538
585
|
}
|
|
539
|
-
/**
|
|
540
|
-
* 获取日志队列统计信息
|
|
541
|
-
*/
|
|
542
|
-
getQueueStats() {
|
|
543
|
-
return {
|
|
544
|
-
queueSize: this.logQueue.length,
|
|
545
|
-
isProcessing: this.isProcessing,
|
|
546
|
-
config: Object.assign({}, this.asyncConfig),
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* 手动刷新日志队列
|
|
551
|
-
*/
|
|
552
|
-
manualFlush() {
|
|
553
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
554
|
-
return new Promise((resolve) => {
|
|
555
|
-
const checkProcessing = () => {
|
|
556
|
-
if (!this.isProcessing) {
|
|
557
|
-
this.flush();
|
|
558
|
-
// 等待刷新完成
|
|
559
|
-
setTimeout(() => resolve(), 100);
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
setTimeout(checkProcessing, 50);
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
checkProcessing();
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* 设置异步日志配置
|
|
571
|
-
*/
|
|
572
|
-
setAsyncConfig(config) {
|
|
573
|
-
Object.assign(this.asyncConfig, config);
|
|
574
|
-
// 重启刷新任务
|
|
575
|
-
if (this.flushTimer) {
|
|
576
|
-
clearInterval(this.flushTimer);
|
|
577
|
-
}
|
|
578
|
-
if (this.asyncConfig.enabled) {
|
|
579
|
-
this.startFlushTask();
|
|
580
|
-
}
|
|
581
|
-
this.logger.log(`Async log config updated: ${JSON.stringify(this.asyncConfig)}`);
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* 转换请求体为字符串
|
|
585
|
-
*/
|
|
586
|
-
sanitizeBodyAsString(data) {
|
|
587
|
-
if (!data)
|
|
588
|
-
return undefined;
|
|
589
|
-
if (typeof data === 'string') {
|
|
590
|
-
return data.length > 5000 ? data.substring(0, 5000) + '...' : data;
|
|
591
|
-
}
|
|
592
|
-
const jsonString = JSON.stringify(data);
|
|
593
|
-
return jsonString.length > 5000
|
|
594
|
-
? jsonString.substring(0, 5000) + '...'
|
|
595
|
-
: jsonString;
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* 清理敏感头信息
|
|
599
|
-
*/
|
|
600
|
-
sanitizeHeaders(headers, sanitizeHeaders) {
|
|
601
|
-
if (!headers)
|
|
602
|
-
return {};
|
|
603
|
-
const sanitized = {};
|
|
604
|
-
const defaultSensitiveHeaders = [
|
|
605
|
-
'authorization',
|
|
606
|
-
'apikey',
|
|
607
|
-
'password',
|
|
608
|
-
'token',
|
|
609
|
-
'secret',
|
|
610
|
-
'cookie',
|
|
611
|
-
'set-cookie',
|
|
612
|
-
'x-api-key',
|
|
613
|
-
'x-auth-token',
|
|
614
|
-
];
|
|
615
|
-
const headersToSanitize = [...defaultSensitiveHeaders, ...sanitizeHeaders];
|
|
616
|
-
Object.keys(headers).forEach((key) => {
|
|
617
|
-
if (headersToSanitize.some((sensitive) => key.toLowerCase().includes(sensitive.toLowerCase()))) {
|
|
618
|
-
sanitized[key] = '[FILTERED]';
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
sanitized[key] = String(headers[key]);
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
return sanitized;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* 清理敏感请求体信息
|
|
628
|
-
*/
|
|
629
|
-
sanitizeBody(body) {
|
|
630
|
-
if (!body)
|
|
631
|
-
return undefined;
|
|
632
|
-
if (typeof body === 'string') {
|
|
633
|
-
try {
|
|
634
|
-
body = JSON.parse(body);
|
|
635
|
-
}
|
|
636
|
-
catch (_a) {
|
|
637
|
-
return body.length > 1000 ? body.substring(0, 1000) + '...' : body;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (typeof body === 'object') {
|
|
641
|
-
const sanitized = Object.assign({}, body);
|
|
642
|
-
const sensitiveFields = [
|
|
643
|
-
'password',
|
|
644
|
-
'secret',
|
|
645
|
-
'token',
|
|
646
|
-
'key',
|
|
647
|
-
'authorization',
|
|
648
|
-
'credential',
|
|
649
|
-
'private',
|
|
650
|
-
'confidential',
|
|
651
|
-
'ssn',
|
|
652
|
-
'creditCard',
|
|
653
|
-
];
|
|
654
|
-
this.sanitizeObject(sanitized, sensitiveFields);
|
|
655
|
-
return sanitized;
|
|
656
|
-
}
|
|
657
|
-
return body;
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* 递归清理对象中的敏感字段
|
|
661
|
-
*/
|
|
662
|
-
sanitizeObject(obj, sensitiveFields) {
|
|
663
|
-
if (typeof obj !== 'object' || obj === null)
|
|
664
|
-
return;
|
|
665
|
-
for (const key in obj) {
|
|
666
|
-
if (sensitiveFields.some((field) => key.toLowerCase().includes(field.toLowerCase()))) {
|
|
667
|
-
obj[key] = '[FILTERED]';
|
|
668
|
-
}
|
|
669
|
-
else if (typeof obj[key] === 'object') {
|
|
670
|
-
this.sanitizeObject(obj[key], sensitiveFields);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
586
|
/**
|
|
675
587
|
* 获取完整URL
|
|
676
588
|
* @param config Axios请求配置
|
|
@@ -137,13 +137,10 @@ class CurlGenerator {
|
|
|
137
137
|
config.url = urlMatch[1];
|
|
138
138
|
}
|
|
139
139
|
// 提取头信息
|
|
140
|
-
const headerMatches = curlCommand.
|
|
140
|
+
const headerMatches = Array.from(curlCommand.matchAll(/-H\s+'([^:]+):\s*([^']+)'/g));
|
|
141
141
|
config.headers = {};
|
|
142
142
|
headerMatches.forEach((header) => {
|
|
143
|
-
|
|
144
|
-
if (headerMatch) {
|
|
145
|
-
config.headers[headerMatch[1]] = headerMatch[2];
|
|
146
|
-
}
|
|
143
|
+
config.headers[header[1]] = header[2];
|
|
147
144
|
});
|
|
148
145
|
// 提取数据
|
|
149
146
|
const dataMatch = curlCommand.match(/-d\s+'([^']+)'/);
|
|
@@ -19,3 +19,4 @@ __exportStar(require("./retry-recorder.util"), exports);
|
|
|
19
19
|
__exportStar(require("./context-extractor.util"), exports);
|
|
20
20
|
__exportStar(require("./call-stack-extractor.util"), exports);
|
|
21
21
|
__exportStar(require("./proxy-environment.util"), exports);
|
|
22
|
+
__exportStar(require("./sanitize.util"), exports);
|
|
@@ -12,6 +12,18 @@ export declare class ProxyEnvironmentParser {
|
|
|
12
12
|
* @returns 解析后的代理配置,如果不应使用代理则返回 false
|
|
13
13
|
*/
|
|
14
14
|
static parseFromEnvironment(protocol?: 'http' | 'https', targetUrl?: string): false | Required<Omit<ProxyConfig, 'enabled' | 'fromEnvironment'>>;
|
|
15
|
+
/**
|
|
16
|
+
* 获取所有代理相关的环境变量
|
|
17
|
+
*/
|
|
18
|
+
static getProxyEnvironmentVariables(): {
|
|
19
|
+
HTTP_PROXY?: string;
|
|
20
|
+
HTTPS_PROXY?: string;
|
|
21
|
+
NO_PROXY?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 检查是否设置了代理环境变量
|
|
25
|
+
*/
|
|
26
|
+
static hasProxyEnvironment(): boolean;
|
|
15
27
|
/**
|
|
16
28
|
* 解析代理 URL
|
|
17
29
|
* 支持格式: http://proxy:port, http://user:pass@proxy:port
|
|
@@ -27,16 +39,4 @@ export declare class ProxyEnvironmentParser {
|
|
|
27
39
|
* 支持精确匹配和 CIDR 表示法
|
|
28
40
|
*/
|
|
29
41
|
private static isIpMatch;
|
|
30
|
-
/**
|
|
31
|
-
* 获取所有代理相关的环境变量
|
|
32
|
-
*/
|
|
33
|
-
static getProxyEnvironmentVariables(): {
|
|
34
|
-
HTTP_PROXY?: string;
|
|
35
|
-
HTTPS_PROXY?: string;
|
|
36
|
-
NO_PROXY?: string;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* 检查是否设置了代理环境变量
|
|
40
|
-
*/
|
|
41
|
-
static hasProxyEnvironment(): boolean;
|
|
42
42
|
}
|
|
@@ -40,6 +40,23 @@ class ProxyEnvironmentParser {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 获取所有代理相关的环境变量
|
|
45
|
+
*/
|
|
46
|
+
static getProxyEnvironmentVariables() {
|
|
47
|
+
return {
|
|
48
|
+
HTTP_PROXY: process.env.HTTP_PROXY || process.env.http_proxy,
|
|
49
|
+
HTTPS_PROXY: process.env.HTTPS_PROXY || process.env.https_proxy,
|
|
50
|
+
NO_PROXY: process.env.NO_PROXY || process.env.no_proxy,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 检查是否设置了代理环境变量
|
|
55
|
+
*/
|
|
56
|
+
static hasProxyEnvironment() {
|
|
57
|
+
const vars = this.getProxyEnvironmentVariables();
|
|
58
|
+
return !!(vars.HTTP_PROXY || vars.HTTPS_PROXY);
|
|
59
|
+
}
|
|
43
60
|
/**
|
|
44
61
|
* 解析代理 URL
|
|
45
62
|
* 支持格式: http://proxy:port, http://user:pass@proxy:port
|
|
@@ -50,7 +67,11 @@ class ProxyEnvironmentParser {
|
|
|
50
67
|
const config = {
|
|
51
68
|
protocol: url.protocol.replace(':', '') || 'http',
|
|
52
69
|
host: url.hostname,
|
|
53
|
-
port: url.port
|
|
70
|
+
port: url.port
|
|
71
|
+
? parseInt(url.port, 10)
|
|
72
|
+
: url.protocol === 'https:'
|
|
73
|
+
? 443
|
|
74
|
+
: 80,
|
|
54
75
|
auth: undefined,
|
|
55
76
|
};
|
|
56
77
|
// 解析认证信息
|
|
@@ -79,7 +100,9 @@ class ProxyEnvironmentParser {
|
|
|
79
100
|
const url = new URL(targetUrl);
|
|
80
101
|
const hostname = url.hostname;
|
|
81
102
|
// NO_PROXY 可以是逗号分隔的列表
|
|
82
|
-
const bypassList = noProxy
|
|
103
|
+
const bypassList = noProxy
|
|
104
|
+
.split(',')
|
|
105
|
+
.map((item) => item.trim().toLowerCase());
|
|
83
106
|
for (const bypass of bypassList) {
|
|
84
107
|
if (!bypass)
|
|
85
108
|
continue;
|
|
@@ -126,23 +149,6 @@ class ProxyEnvironmentParser {
|
|
|
126
149
|
}
|
|
127
150
|
return false;
|
|
128
151
|
}
|
|
129
|
-
/**
|
|
130
|
-
* 获取所有代理相关的环境变量
|
|
131
|
-
*/
|
|
132
|
-
static getProxyEnvironmentVariables() {
|
|
133
|
-
return {
|
|
134
|
-
HTTP_PROXY: process.env.HTTP_PROXY || process.env.http_proxy,
|
|
135
|
-
HTTPS_PROXY: process.env.HTTPS_PROXY || process.env.https_proxy,
|
|
136
|
-
NO_PROXY: process.env.NO_PROXY || process.env.no_proxy,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* 检查是否设置了代理环境变量
|
|
141
|
-
*/
|
|
142
|
-
static hasProxyEnvironment() {
|
|
143
|
-
const vars = this.getProxyEnvironmentVariables();
|
|
144
|
-
return !!(vars.HTTP_PROXY || vars.HTTPS_PROXY);
|
|
145
|
-
}
|
|
146
152
|
}
|
|
147
153
|
exports.ProxyEnvironmentParser = ProxyEnvironmentParser;
|
|
148
154
|
ProxyEnvironmentParser.logger = new common_1.Logger(ProxyEnvironmentParser.name);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RetryRecorder = void 0;
|
|
4
|
+
const sanitize_util_1 = require("./sanitize.util");
|
|
4
5
|
/**
|
|
5
6
|
* 请求重试记录器
|
|
6
7
|
*/
|
|
@@ -22,7 +23,7 @@ class RetryRecorder {
|
|
|
22
23
|
requestConfig: {
|
|
23
24
|
method: ((_a = config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN',
|
|
24
25
|
url: config.url || '',
|
|
25
|
-
headers:
|
|
26
|
+
headers: sanitize_util_1.SanitizeUtil.sanitizeHeaders(config.headers || {}),
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
29
|
}
|
|
@@ -83,32 +84,6 @@ class RetryRecorder {
|
|
|
83
84
|
}
|
|
84
85
|
return `HTTP error: ${status}`;
|
|
85
86
|
}
|
|
86
|
-
/**
|
|
87
|
-
* 过滤敏感头信息
|
|
88
|
-
*/
|
|
89
|
-
static sanitizeHeaders(headers) {
|
|
90
|
-
const sanitized = {};
|
|
91
|
-
const sensitiveKeys = [
|
|
92
|
-
'authorization',
|
|
93
|
-
'apikey',
|
|
94
|
-
'password',
|
|
95
|
-
'secret',
|
|
96
|
-
'token',
|
|
97
|
-
'x-api-key',
|
|
98
|
-
'x-auth-token',
|
|
99
|
-
'cookie',
|
|
100
|
-
'set-cookie',
|
|
101
|
-
];
|
|
102
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
103
|
-
if (sensitiveKeys.some((sensitive) => key.toLowerCase().includes(sensitive))) {
|
|
104
|
-
sanitized[key] = '[FILTERED]';
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
sanitized[key] = value;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
return sanitized;
|
|
111
|
-
}
|
|
112
87
|
/**
|
|
113
88
|
* 添加重试记录
|
|
114
89
|
*/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 请求数据脱敏工具
|
|
3
|
+
* 统一处理 headers、body、query string 的敏感信息过滤
|
|
4
|
+
*/
|
|
5
|
+
export declare class SanitizeUtil {
|
|
6
|
+
/**
|
|
7
|
+
* 默认敏感字段列表
|
|
8
|
+
*/
|
|
9
|
+
private static readonly DEFAULT_SENSITIVE_FIELDS;
|
|
10
|
+
/**
|
|
11
|
+
* 脱敏 headers
|
|
12
|
+
* @param headers - 原始 headers 对象
|
|
13
|
+
* @param sensitiveFields - 自定义敏感字段列表
|
|
14
|
+
* @returns 脱敏后的 headers
|
|
15
|
+
*/
|
|
16
|
+
static sanitizeHeaders(headers: any, sensitiveFields?: string[]): Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* 脱敏 body
|
|
19
|
+
* @param body - 原始 body 数据
|
|
20
|
+
* @param sensitiveFields - 自定义敏感字段列表
|
|
21
|
+
* @returns 脱敏后的 body
|
|
22
|
+
*/
|
|
23
|
+
static sanitizeBody(body: any, sensitiveFields?: string[]): any;
|
|
24
|
+
/**
|
|
25
|
+
* 脱敏 URL 中的 query string
|
|
26
|
+
* @param url - 原始 URL
|
|
27
|
+
* @param sensitiveFields - 自定义敏感字段列表
|
|
28
|
+
* @returns 脱敏后的 URL
|
|
29
|
+
*/
|
|
30
|
+
static sanitizeQueryString(url: string, sensitiveFields?: string[]): string;
|
|
31
|
+
/**
|
|
32
|
+
* 脱敏 params 对象
|
|
33
|
+
* @param params - 原始 params 对象
|
|
34
|
+
* @param sensitiveFields - 自定义敏感字段列表
|
|
35
|
+
* @returns 脱敏后的 params
|
|
36
|
+
*/
|
|
37
|
+
static sanitizeParams(params: Record<string, any>, sensitiveFields?: string[]): Record<string, any>;
|
|
38
|
+
/**
|
|
39
|
+
* 将 body 转换为字符串并脱敏
|
|
40
|
+
* @param data - 原始数据
|
|
41
|
+
* @param sensitiveFields - 自定义敏感字段列表
|
|
42
|
+
* @returns 脱敏后的字符串
|
|
43
|
+
*/
|
|
44
|
+
static sanitizeBodyAsString(data: any, sensitiveFields?: string[]): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* 判断字段是否为敏感字段
|
|
47
|
+
* @param key - 字段名
|
|
48
|
+
* @param sensitiveFields - 敏感字段列表
|
|
49
|
+
* @returns 是否为敏感字段
|
|
50
|
+
*/
|
|
51
|
+
private static isSensitiveField;
|
|
52
|
+
/**
|
|
53
|
+
* 递归脱敏对象中的敏感字段
|
|
54
|
+
* @param obj - 目标对象
|
|
55
|
+
* @param sensitiveFields - 敏感字段列表
|
|
56
|
+
*/
|
|
57
|
+
private static sanitizeObject;
|
|
58
|
+
}
|