@cumulus/es-client 20.1.3-alpha.2 → 20.2.0
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/package.json +8 -8
- package/search.js +108 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cumulus/es-client",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.2.0",
|
|
4
4
|
"description": "Utilities for working with Elasticsearch",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CUMULUS",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"license": "Apache-2.0",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@aws-sdk/credential-providers": "^3.621.0",
|
|
37
|
-
"@cumulus/common": "20.
|
|
38
|
-
"@cumulus/errors": "20.
|
|
39
|
-
"@cumulus/logger": "20.
|
|
40
|
-
"@cumulus/message": "20.
|
|
37
|
+
"@cumulus/common": "20.2.0",
|
|
38
|
+
"@cumulus/errors": "20.2.0",
|
|
39
|
+
"@cumulus/logger": "20.2.0",
|
|
40
|
+
"@cumulus/message": "20.2.0",
|
|
41
41
|
"@elastic/elasticsearch": "^5.6.20",
|
|
42
42
|
"aws4": "^1.12.0",
|
|
43
43
|
"lodash": "~4.17.21",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"p-limit": "^1.2.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@cumulus/aws-client": "20.
|
|
49
|
-
"@cumulus/test-data": "20.
|
|
48
|
+
"@cumulus/aws-client": "20.2.0",
|
|
49
|
+
"@cumulus/test-data": "20.2.0",
|
|
50
50
|
"p-each-series": "^2.1.0"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "8afcafd2b63778a1ea666163aa268c2dfd7d60ec"
|
|
53
53
|
}
|
package/search.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
const has = require('lodash/has');
|
|
11
11
|
const omit = require('lodash/omit');
|
|
12
|
+
const isString = require('lodash/isString');
|
|
13
|
+
const isError = require('lodash/isError');
|
|
12
14
|
const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
|
|
13
15
|
const elasticsearch = require('@elastic/elasticsearch');
|
|
14
16
|
|
|
@@ -29,6 +31,68 @@ const multipleRecordFoundString = 'More than one record was found!';
|
|
|
29
31
|
const recordNotFoundString = 'Record not found';
|
|
30
32
|
const logger = new Logger({ sender: '@cumulus/es-client/search' });
|
|
31
33
|
|
|
34
|
+
class HttpError extends Error {
|
|
35
|
+
constructor(statusCode, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.statusCode = statusCode;
|
|
38
|
+
this.name = 'HttpError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sanitizes sensitive data in error messages or logs
|
|
44
|
+
*
|
|
45
|
+
* @param {Error|string} input - The error object or message to sanitize
|
|
46
|
+
* @returns {Error|string} - Sanitized error or message
|
|
47
|
+
*/
|
|
48
|
+
const sanitizeSensitive = (input) => {
|
|
49
|
+
const sensitiveFields = [
|
|
50
|
+
`${process.env.METRICS_ES_USER}:${process.env.METRICS_ES_PASS}`,
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
|
|
53
|
+
let message = isString(input) ? input : input.message || input.toString();
|
|
54
|
+
|
|
55
|
+
const escapeRegExp = (string) => string.replace(/[$()*+.?[\\\]^{|}-]/g, '\\$&');
|
|
56
|
+
|
|
57
|
+
sensitiveFields.forEach((field) => {
|
|
58
|
+
if (field) {
|
|
59
|
+
const pattern = `(^|\\s|[^a-zA-Z0-9_])(${escapeRegExp(field)})($|\\s|[^a-zA-Z0-9_])`;
|
|
60
|
+
message = message.replace(new RegExp(pattern, 'g'), '$1*****$3');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (isString(input)) {
|
|
65
|
+
return message;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const sanitizedError = new Error(message);
|
|
69
|
+
sanitizedError.stack = input.stack;
|
|
70
|
+
return sanitizedError;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Custom logger for elasticsearch client to sanitize sensitive data
|
|
75
|
+
*/
|
|
76
|
+
class EsCustomLogger {
|
|
77
|
+
constructor() {
|
|
78
|
+
this.levels = ['error', 'warning']; // Log only errors and warnings
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
error(message) {
|
|
82
|
+
logger.error(sanitizeSensitive(message));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
warning(message) {
|
|
86
|
+
logger.warn(sanitizeSensitive(message));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
info() {}
|
|
90
|
+
|
|
91
|
+
debug() {}
|
|
92
|
+
|
|
93
|
+
trace() {}
|
|
94
|
+
}
|
|
95
|
+
|
|
32
96
|
/**
|
|
33
97
|
* Returns the local address of elasticsearch based on
|
|
34
98
|
* environment variables
|
|
@@ -102,12 +166,14 @@ const esMetricsConfig = () => {
|
|
|
102
166
|
throw new Error('ELK Metrics stack not configured');
|
|
103
167
|
}
|
|
104
168
|
|
|
105
|
-
const
|
|
106
|
-
|
|
169
|
+
const encodedUser = encodeURIComponent(process.env.METRICS_ES_USER);
|
|
170
|
+
const encodedPass = encodeURIComponent(process.env.METRICS_ES_PASS);
|
|
171
|
+
const node = `https://${encodedUser}:${encodedPass}@${process.env.METRICS_ES_HOST}`;
|
|
107
172
|
|
|
108
173
|
return {
|
|
109
174
|
node,
|
|
110
175
|
requestTimeout: 50000,
|
|
176
|
+
log: EsCustomLogger,
|
|
111
177
|
};
|
|
112
178
|
};
|
|
113
179
|
|
|
@@ -329,6 +395,7 @@ class BaseSearch {
|
|
|
329
395
|
}
|
|
330
396
|
|
|
331
397
|
async get(id, parentId) {
|
|
398
|
+
const esCustomLogger = new EsCustomLogger();
|
|
332
399
|
const body = {
|
|
333
400
|
query: {
|
|
334
401
|
bool: {
|
|
@@ -358,23 +425,31 @@ class BaseSearch {
|
|
|
358
425
|
await this.initializeEsClient();
|
|
359
426
|
}
|
|
360
427
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
428
|
+
try {
|
|
429
|
+
const result = await this.client.search({
|
|
430
|
+
index: this.index,
|
|
431
|
+
type: this.type,
|
|
432
|
+
body,
|
|
433
|
+
})
|
|
434
|
+
.then((response) => response.body);
|
|
435
|
+
|
|
436
|
+
if (result.hits.total > 1) {
|
|
437
|
+
return { detail: multipleRecordFoundString };
|
|
438
|
+
}
|
|
439
|
+
if (result.hits.total === 0) {
|
|
440
|
+
return { detail: recordNotFoundString };
|
|
441
|
+
}
|
|
367
442
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
443
|
+
const resp = result.hits.hits[0]._source;
|
|
444
|
+
resp._id = result.hits.hits[0]._id;
|
|
445
|
+
return resp;
|
|
446
|
+
} catch (error) {
|
|
447
|
+
esCustomLogger.error(sanitizeSensitive(error));
|
|
448
|
+
if (error.meta?.statusCode === 401) {
|
|
449
|
+
throw new HttpError(401, 'Invalid credentials');
|
|
450
|
+
}
|
|
451
|
+
throw sanitizeSensitive(error);
|
|
373
452
|
}
|
|
374
|
-
|
|
375
|
-
const resp = result.hits.hits[0]._source;
|
|
376
|
-
resp._id = result.hits.hits[0]._id;
|
|
377
|
-
return resp;
|
|
378
453
|
}
|
|
379
454
|
|
|
380
455
|
async exists(id, parentId) {
|
|
@@ -408,7 +483,13 @@ class BaseSearch {
|
|
|
408
483
|
results: hits.map((s) => s._source),
|
|
409
484
|
};
|
|
410
485
|
} catch (error) {
|
|
411
|
-
|
|
486
|
+
const esCustomLogger = new EsCustomLogger();
|
|
487
|
+
esCustomLogger.error(sanitizeSensitive(error));
|
|
488
|
+
|
|
489
|
+
if (error.meta?.statusCode === 401) {
|
|
490
|
+
throw new HttpError(401, 'Invalid credentials');
|
|
491
|
+
}
|
|
492
|
+
throw sanitizeSensitive(error);
|
|
412
493
|
}
|
|
413
494
|
}
|
|
414
495
|
|
|
@@ -431,7 +512,13 @@ class BaseSearch {
|
|
|
431
512
|
counts: result.body.aggregations,
|
|
432
513
|
};
|
|
433
514
|
} catch (error) {
|
|
434
|
-
|
|
515
|
+
const esCustomLogger = new EsCustomLogger();
|
|
516
|
+
esCustomLogger.error(sanitizeSensitive(error));
|
|
517
|
+
|
|
518
|
+
if (error.meta?.statusCode === 401) {
|
|
519
|
+
throw new HttpError(401, 'Invalid credentials');
|
|
520
|
+
}
|
|
521
|
+
throw sanitizeSensitive(error);
|
|
435
522
|
}
|
|
436
523
|
}
|
|
437
524
|
}
|
|
@@ -461,4 +548,6 @@ module.exports = {
|
|
|
461
548
|
multipleRecordFoundString,
|
|
462
549
|
recordNotFoundString,
|
|
463
550
|
getLocalEsHost,
|
|
551
|
+
sanitizeSensitive,
|
|
552
|
+
isError,
|
|
464
553
|
};
|