@wdio/logger 9.4.4 → 9.15.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/README.md +48 -0
- package/build/browser.d.ts +2 -0
- package/build/browser.d.ts.map +1 -1
- package/build/browser.js +4 -0
- package/build/index.cjs +97 -15
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +88 -12
- package/build/utils.d.ts +21 -0
- package/build/utils.d.ts.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -70,3 +70,51 @@ const uploadInterval = setInterval(() => {
|
|
|
70
70
|
}
|
|
71
71
|
}, 100);
|
|
72
72
|
```
|
|
73
|
+
|
|
74
|
+
## Masking Patterns
|
|
75
|
+
|
|
76
|
+
For more secure logging, `setMaskingPatterns`, `WDIO_LOG_MASKING_PATTERNS` or `maskingPatterns` can obfuscate sensitive information from the log.
|
|
77
|
+
For example, we can replace `--key=MySecretKey` with `--key=**MASKED**` to hide your cloud service access key or secret key
|
|
78
|
+
- The regular expression pattern must be provided as a string similar as a RegEx but as string type, for example, `--key=[^ ]*`
|
|
79
|
+
- It support flags and capturing groups like `/--key=([^ ]*)/i`
|
|
80
|
+
- Multiple patterns are separated by a comma, like `--key=([^ ]*),secrets=([^ ]*)`
|
|
81
|
+
- If no capturing group is provided, the entire matching string of the pattern is masked
|
|
82
|
+
- If one or more capturing groups are provided, we replace all the matching groups with `**MASKED**`
|
|
83
|
+
- If there are multiple matches for a single group, we replace them all, too
|
|
84
|
+
- Support both masking in a file and the console
|
|
85
|
+
- Note: In the console, when masking, some colors get stripped, which is a known limitation
|
|
86
|
+
|
|
87
|
+
`setMaskingPatterns` example
|
|
88
|
+
```javascript
|
|
89
|
+
import logger from '@wdio/logger';
|
|
90
|
+
|
|
91
|
+
// Default for all loggers
|
|
92
|
+
logger.setMaskingPatterns('/--key=([^ ]*)/i,/--secrets=([^ ]*)/i')
|
|
93
|
+
|
|
94
|
+
// For a specific logger
|
|
95
|
+
logger.setMaskingPatterns({'internal' : '/--key=([^ ]*)/i,/--secrets=([^ ]*)/i'})
|
|
96
|
+
const log = logger('internal');
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Using wdio config from a `conf.ts` file, we can also configure masking patterns
|
|
100
|
+
```javascript
|
|
101
|
+
export const config: WebdriverIO.Config = {
|
|
102
|
+
/**
|
|
103
|
+
* test configurations
|
|
104
|
+
*/
|
|
105
|
+
logLevel: 'debug',
|
|
106
|
+
maskingPatterns: '/--key=([^ ]*)/i,/--secrets=([^ ]*)/i',
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Below are examples with the environment variable `WDIO_LOG_MASKING_PATTERNS` in the code directly:
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Using environment variable in code
|
|
114
|
+
process.env.WDIO_LOG_MASKING_PATTERNS = '/--key=([^ ]*)/i,/--secrets=([^ ]*)/i'
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or before your command line
|
|
118
|
+
```shell
|
|
119
|
+
WDIO_LOG_MASKING_PATTERNS='RESULT ([^ ]*)' npx wdio run ./wdio/wdio.conf.ts
|
|
120
|
+
```
|
package/build/browser.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { Logger } from './index.js';
|
|
2
|
+
export declare const SENSITIVE_DATA_REPLACER = "**MASKED**";
|
|
2
3
|
/**
|
|
3
4
|
* This implementation of the Logger package is a simple adptation to run it within
|
|
4
5
|
* a browser environment.
|
|
@@ -7,6 +8,7 @@ declare function getLogger(component: string): Console;
|
|
|
7
8
|
declare namespace getLogger {
|
|
8
9
|
var setLevel: () => void;
|
|
9
10
|
var setLogLevelsConfig: () => void;
|
|
11
|
+
var setMaskingPatterns: () => void;
|
|
10
12
|
var waitForBuffer: () => void;
|
|
11
13
|
var clearLogger: () => void;
|
|
12
14
|
}
|
package/build/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC;;;GAGG;AACH,iBAAwB,SAAS,CAAE,SAAS,EAAE,MAAM,WAYnD;kBAZuB,SAAS
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAEnD;;;GAGG;AACH,iBAAwB,SAAS,CAAE,SAAS,EAAE,MAAM,WAYnD;kBAZuB,SAAS;;;;;;;eAAT,SAAS"}
|
package/build/browser.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/browser.ts
|
|
2
2
|
var LOG_METHODS = ["error", "warn", "info", "debug", "trace", "silent"];
|
|
3
|
+
var SENSITIVE_DATA_REPLACER = "**MASKED**";
|
|
3
4
|
function getLogger(component) {
|
|
4
5
|
return LOG_METHODS.reduce((acc, cur) => {
|
|
5
6
|
const prop = cur;
|
|
@@ -13,10 +14,13 @@ getLogger.setLevel = () => {
|
|
|
13
14
|
};
|
|
14
15
|
getLogger.setLogLevelsConfig = () => {
|
|
15
16
|
};
|
|
17
|
+
getLogger.setMaskingPatterns = () => {
|
|
18
|
+
};
|
|
16
19
|
getLogger.waitForBuffer = () => {
|
|
17
20
|
};
|
|
18
21
|
getLogger.clearLogger = () => {
|
|
19
22
|
};
|
|
20
23
|
export {
|
|
24
|
+
SENSITIVE_DATA_REPLACER,
|
|
21
25
|
getLogger as default
|
|
22
26
|
};
|
package/build/index.cjs
CHANGED
|
@@ -29,11 +29,63 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
|
-
var
|
|
33
|
-
__export(
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
SENSITIVE_DATA_REPLACER: () => SENSITIVE_DATA_REPLACER,
|
|
34
35
|
default: () => getLogger
|
|
35
36
|
});
|
|
36
|
-
module.exports = __toCommonJS(
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/utils.ts
|
|
40
|
+
var SENSITIVE_DATA_REPLACER = "**MASKED**";
|
|
41
|
+
var skipError = (aFunction) => {
|
|
42
|
+
try {
|
|
43
|
+
return aFunction();
|
|
44
|
+
} catch {
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var parseMaskingPatterns = (maskingRegexString) => {
|
|
49
|
+
if (typeof maskingRegexString !== "string") {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
const regexStrings = maskingRegexString?.split(/,\s*/).filter((regexStr) => regexStr.trim() !== "");
|
|
53
|
+
return regexStrings?.map((regexStr) => {
|
|
54
|
+
const regexParts = regexStr.match(/^\/(.*?)\/([gimsuy]*)$/);
|
|
55
|
+
if (!regexParts) {
|
|
56
|
+
return skipError(() => new RegExp(regexStr));
|
|
57
|
+
} else if (regexParts?.[1]) {
|
|
58
|
+
return skipError(() => regexParts[2] ? new RegExp(regexParts[1], regexParts[2]) : new RegExp(regexParts[1]));
|
|
59
|
+
}
|
|
60
|
+
return void 0;
|
|
61
|
+
}).filter((regex) => regex !== void 0);
|
|
62
|
+
};
|
|
63
|
+
var mask = (text, maskingPatterns) => {
|
|
64
|
+
if (!maskingPatterns || typeof text !== "string") {
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
const endsWithNewline = text.endsWith("\n");
|
|
68
|
+
let maskedText = text;
|
|
69
|
+
maskingPatterns.forEach((maskingRegex) => {
|
|
70
|
+
maskedText = maskedText.replace(maskingRegex, (fullMatch, ...capturedGroupsAndMore) => {
|
|
71
|
+
const capturedGroups = capturedGroupsAndMore.slice(0, capturedGroupsAndMore.length - 2);
|
|
72
|
+
if (capturedGroups.length === 0) {
|
|
73
|
+
return SENSITIVE_DATA_REPLACER;
|
|
74
|
+
}
|
|
75
|
+
let matchedMaskedText = fullMatch;
|
|
76
|
+
capturedGroups.forEach((group) => {
|
|
77
|
+
matchedMaskedText = matchedMaskedText.replace(group, SENSITIVE_DATA_REPLACER);
|
|
78
|
+
});
|
|
79
|
+
return matchedMaskedText;
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
if (endsWithNewline && !maskedText.endsWith("\n")) {
|
|
83
|
+
maskedText += "\n";
|
|
84
|
+
}
|
|
85
|
+
return maskedText;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/index.ts
|
|
37
89
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
38
90
|
var import_node_util = __toESM(require("node:util"), 1);
|
|
39
91
|
var import_loglevel = __toESM(require("loglevel"), 1);
|
|
@@ -84,10 +136,11 @@ var SERIALIZERS = [{
|
|
|
84
136
|
}];
|
|
85
137
|
var loggers = import_loglevel.default.getLoggers();
|
|
86
138
|
var logLevelsConfig = {};
|
|
139
|
+
var maskingPatternsConfig = {};
|
|
87
140
|
var logCache = /* @__PURE__ */ new Set();
|
|
88
141
|
var logFile;
|
|
89
142
|
var originalFactory = import_loglevel.default.methodFactory;
|
|
90
|
-
var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
143
|
+
var wdioLoggerMethodFactory = (wdioLogger) => function(methodName, logLevel, loggerName) {
|
|
91
144
|
const rawMethod = originalFactory(methodName, logLevel, loggerName);
|
|
92
145
|
return (...args) => {
|
|
93
146
|
if (!logFile && process.env.WDIO_LOG_PATH) {
|
|
@@ -106,8 +159,9 @@ var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
|
106
159
|
}
|
|
107
160
|
return arg;
|
|
108
161
|
});
|
|
109
|
-
const
|
|
162
|
+
const unmaskedLogText = (0, import_strip_ansi.default)(`${import_node_util.default.format.apply(this, args)}
|
|
110
163
|
`);
|
|
164
|
+
const maskedLogText = mask(unmaskedLogText, wdioLogger.maskingPatterns);
|
|
111
165
|
if (logFile && logFile.writable) {
|
|
112
166
|
if (logCache.size) {
|
|
113
167
|
logCache.forEach((log2) => {
|
|
@@ -117,13 +171,17 @@ var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
|
117
171
|
});
|
|
118
172
|
logCache.clear();
|
|
119
173
|
}
|
|
120
|
-
if (!logsContainInitPackageError(
|
|
121
|
-
return logFile.write(
|
|
174
|
+
if (!logsContainInitPackageError(unmaskedLogText)) {
|
|
175
|
+
return logFile.write(maskedLogText);
|
|
122
176
|
}
|
|
123
|
-
logFile.write(
|
|
177
|
+
logFile.write(maskedLogText);
|
|
178
|
+
}
|
|
179
|
+
logCache.add(maskedLogText);
|
|
180
|
+
if (maskedLogText === unmaskedLogText) {
|
|
181
|
+
rawMethod(...args);
|
|
182
|
+
} else {
|
|
183
|
+
rawMethod(maskedLogText.replace(/\n$/, ""));
|
|
124
184
|
}
|
|
125
|
-
logCache.add(logText);
|
|
126
|
-
rawMethod(...args);
|
|
127
185
|
};
|
|
128
186
|
};
|
|
129
187
|
var progress = function(data) {
|
|
@@ -147,16 +205,18 @@ function getLogger(name) {
|
|
|
147
205
|
logLevel = logLevelsConfig[logLevelName];
|
|
148
206
|
}
|
|
149
207
|
loggers[name] = import_loglevel.default.getLogger(name);
|
|
150
|
-
loggers[name]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
208
|
+
const logger = loggers[name];
|
|
209
|
+
logger.setLevel(logLevel);
|
|
210
|
+
logger.maskingPatterns = maskingPatternsConfig[name] ?? parseMaskingPatterns(process.env.WDIO_LOG_MASKING_PATTERNS);
|
|
211
|
+
logger.progress = progress;
|
|
212
|
+
logger.methodFactory = wdioLoggerMethodFactory(logger);
|
|
213
|
+
import_loglevel_plugin_prefix.default.apply(logger, {
|
|
154
214
|
template: "%t %l %n:",
|
|
155
215
|
timestampFormatter: (date) => import_chalk.default.gray(date.toISOString()),
|
|
156
216
|
levelFormatter: (level) => import_chalk.default[COLORS[level]](level.toUpperCase()),
|
|
157
217
|
nameFormatter: (name2) => import_chalk.default.whiteBright(name2)
|
|
158
218
|
});
|
|
159
|
-
return
|
|
219
|
+
return logger;
|
|
160
220
|
}
|
|
161
221
|
getLogger.waitForBuffer = async () => new Promise((resolve) => {
|
|
162
222
|
if (logFile && Array.isArray(logFile.writableBuffer) && logFile.writableBuffer.length !== 0) {
|
|
@@ -189,6 +249,24 @@ getLogger.setLogLevelsConfig = (logLevels = {}, wdioLogLevel = DEFAULT_LEVEL) =>
|
|
|
189
249
|
loggers[logName].setLevel(logLevel);
|
|
190
250
|
});
|
|
191
251
|
};
|
|
252
|
+
getLogger.setMaskingPatterns = (pattern) => {
|
|
253
|
+
if (typeof pattern === "string") {
|
|
254
|
+
if (process.env.WDIO_LOG_MASKING_PATTERNS === void 0) {
|
|
255
|
+
process.env.WDIO_LOG_MASKING_PATTERNS = pattern;
|
|
256
|
+
}
|
|
257
|
+
} else if (typeof pattern === "object") {
|
|
258
|
+
maskingPatternsConfig = Object.entries(pattern).reduce((acc, [logName, maskingPatternsString]) => {
|
|
259
|
+
acc[logName] = parseMaskingPatterns(maskingPatternsString);
|
|
260
|
+
return acc;
|
|
261
|
+
}, maskingPatternsConfig);
|
|
262
|
+
} else {
|
|
263
|
+
throw new Error(`Invalid pattern property, expected \`string\` or \`Record<string, string>\` but received \`${typeof pattern}\``);
|
|
264
|
+
}
|
|
265
|
+
Object.keys(loggers).forEach((logName) => {
|
|
266
|
+
const maskingPatterns = maskingPatternsConfig[logName] ?? parseMaskingPatterns(process.env.WDIO_LOG_MASKING_PATTERNS);
|
|
267
|
+
loggers[logName].maskingPatterns = maskingPatterns;
|
|
268
|
+
});
|
|
269
|
+
};
|
|
192
270
|
var getLogLevelName = (logName) => logName.split(":").shift();
|
|
193
271
|
function logsContainInitPackageError(logText) {
|
|
194
272
|
return ERROR_LOG_VALIDATOR.every((pattern) => logText.includes(pattern));
|
|
@@ -199,3 +277,7 @@ var ERROR_LOG_VALIDATOR = [
|
|
|
199
277
|
"nor as community package",
|
|
200
278
|
"Please make sure you have it installed"
|
|
201
279
|
];
|
|
280
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
281
|
+
0 && (module.exports = {
|
|
282
|
+
SENSITIVE_DATA_REPLACER
|
|
283
|
+
});
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export { SENSITIVE_DATA_REPLACER } from './utils.js';
|
|
1
2
|
import log from 'loglevel';
|
|
2
3
|
interface LoggerInterface extends log.Logger {
|
|
4
|
+
maskingPatterns: RegExp[] | undefined;
|
|
3
5
|
progress(...msg: string[]): void;
|
|
4
6
|
}
|
|
5
7
|
declare function getLogger(name: string): LoggerInterface;
|
|
@@ -8,7 +10,10 @@ declare namespace getLogger {
|
|
|
8
10
|
var setLevel: (name: string, level: log.LogLevelDesc) => void;
|
|
9
11
|
var clearLogger: () => void;
|
|
10
12
|
var setLogLevelsConfig: (logLevels?: Record<string, log.LogLevelDesc>, wdioLogLevel?: log.LogLevelDesc) => void;
|
|
13
|
+
var setMaskingPatterns: (pattern: MaskPattern | MaskPatternForSpecificLogger) => void;
|
|
11
14
|
}
|
|
12
15
|
export default getLogger;
|
|
16
|
+
type MaskPattern = string;
|
|
17
|
+
type MaskPatternForSpecificLogger = Record<string, MaskPattern>;
|
|
13
18
|
export type Logger = LoggerInterface;
|
|
14
19
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAKpD,OAAO,GAAG,MAAM,UAAU,CAAA;AAuD1B,UAAU,eAAgB,SAAQ,GAAG,CAAC,MAAM;IACxC,eAAe,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACpC;AAwFD,iBAAwB,SAAS,CAAE,IAAI,EAAE,MAAM,mBA8B9C;kBA9BuB,SAAS;;yBA6CL,MAAM,SAAS,GAAG,CAAC,YAAY;;yCAOhB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,iBAAqB,GAAG,CAAC,YAAY;sCAmCvE,WAAW,GAAG,4BAA4B;;eAvF3D,SAAS;AAqFjC,KAAK,WAAW,GAAG,MAAM,CAAA;AACzB,KAAK,4BAA4B,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAoC/D,MAAM,MAAM,MAAM,GAAG,eAAe,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,3 +1,52 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
var SENSITIVE_DATA_REPLACER = "**MASKED**";
|
|
3
|
+
var skipError = (aFunction) => {
|
|
4
|
+
try {
|
|
5
|
+
return aFunction();
|
|
6
|
+
} catch {
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var parseMaskingPatterns = (maskingRegexString) => {
|
|
11
|
+
if (typeof maskingRegexString !== "string") {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
const regexStrings = maskingRegexString?.split(/,\s*/).filter((regexStr) => regexStr.trim() !== "");
|
|
15
|
+
return regexStrings?.map((regexStr) => {
|
|
16
|
+
const regexParts = regexStr.match(/^\/(.*?)\/([gimsuy]*)$/);
|
|
17
|
+
if (!regexParts) {
|
|
18
|
+
return skipError(() => new RegExp(regexStr));
|
|
19
|
+
} else if (regexParts?.[1]) {
|
|
20
|
+
return skipError(() => regexParts[2] ? new RegExp(regexParts[1], regexParts[2]) : new RegExp(regexParts[1]));
|
|
21
|
+
}
|
|
22
|
+
return void 0;
|
|
23
|
+
}).filter((regex) => regex !== void 0);
|
|
24
|
+
};
|
|
25
|
+
var mask = (text, maskingPatterns) => {
|
|
26
|
+
if (!maskingPatterns || typeof text !== "string") {
|
|
27
|
+
return text;
|
|
28
|
+
}
|
|
29
|
+
const endsWithNewline = text.endsWith("\n");
|
|
30
|
+
let maskedText = text;
|
|
31
|
+
maskingPatterns.forEach((maskingRegex) => {
|
|
32
|
+
maskedText = maskedText.replace(maskingRegex, (fullMatch, ...capturedGroupsAndMore) => {
|
|
33
|
+
const capturedGroups = capturedGroupsAndMore.slice(0, capturedGroupsAndMore.length - 2);
|
|
34
|
+
if (capturedGroups.length === 0) {
|
|
35
|
+
return SENSITIVE_DATA_REPLACER;
|
|
36
|
+
}
|
|
37
|
+
let matchedMaskedText = fullMatch;
|
|
38
|
+
capturedGroups.forEach((group) => {
|
|
39
|
+
matchedMaskedText = matchedMaskedText.replace(group, SENSITIVE_DATA_REPLACER);
|
|
40
|
+
});
|
|
41
|
+
return matchedMaskedText;
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
if (endsWithNewline && !maskedText.endsWith("\n")) {
|
|
45
|
+
maskedText += "\n";
|
|
46
|
+
}
|
|
47
|
+
return maskedText;
|
|
48
|
+
};
|
|
49
|
+
|
|
1
50
|
// src/index.ts
|
|
2
51
|
import fs from "node:fs";
|
|
3
52
|
import util from "node:util";
|
|
@@ -49,10 +98,11 @@ var SERIALIZERS = [{
|
|
|
49
98
|
}];
|
|
50
99
|
var loggers = log.getLoggers();
|
|
51
100
|
var logLevelsConfig = {};
|
|
101
|
+
var maskingPatternsConfig = {};
|
|
52
102
|
var logCache = /* @__PURE__ */ new Set();
|
|
53
103
|
var logFile;
|
|
54
104
|
var originalFactory = log.methodFactory;
|
|
55
|
-
var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
105
|
+
var wdioLoggerMethodFactory = (wdioLogger) => function(methodName, logLevel, loggerName) {
|
|
56
106
|
const rawMethod = originalFactory(methodName, logLevel, loggerName);
|
|
57
107
|
return (...args) => {
|
|
58
108
|
if (!logFile && process.env.WDIO_LOG_PATH) {
|
|
@@ -71,8 +121,9 @@ var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
|
71
121
|
}
|
|
72
122
|
return arg;
|
|
73
123
|
});
|
|
74
|
-
const
|
|
124
|
+
const unmaskedLogText = ansiStrip(`${util.format.apply(this, args)}
|
|
75
125
|
`);
|
|
126
|
+
const maskedLogText = mask(unmaskedLogText, wdioLogger.maskingPatterns);
|
|
76
127
|
if (logFile && logFile.writable) {
|
|
77
128
|
if (logCache.size) {
|
|
78
129
|
logCache.forEach((log2) => {
|
|
@@ -82,13 +133,17 @@ var wdioLoggerMethodFactory = function(methodName, logLevel, loggerName) {
|
|
|
82
133
|
});
|
|
83
134
|
logCache.clear();
|
|
84
135
|
}
|
|
85
|
-
if (!logsContainInitPackageError(
|
|
86
|
-
return logFile.write(
|
|
136
|
+
if (!logsContainInitPackageError(unmaskedLogText)) {
|
|
137
|
+
return logFile.write(maskedLogText);
|
|
87
138
|
}
|
|
88
|
-
logFile.write(
|
|
139
|
+
logFile.write(maskedLogText);
|
|
140
|
+
}
|
|
141
|
+
logCache.add(maskedLogText);
|
|
142
|
+
if (maskedLogText === unmaskedLogText) {
|
|
143
|
+
rawMethod(...args);
|
|
144
|
+
} else {
|
|
145
|
+
rawMethod(maskedLogText.replace(/\n$/, ""));
|
|
89
146
|
}
|
|
90
|
-
logCache.add(logText);
|
|
91
|
-
rawMethod(...args);
|
|
92
147
|
};
|
|
93
148
|
};
|
|
94
149
|
var progress = function(data) {
|
|
@@ -112,16 +167,18 @@ function getLogger(name) {
|
|
|
112
167
|
logLevel = logLevelsConfig[logLevelName];
|
|
113
168
|
}
|
|
114
169
|
loggers[name] = log.getLogger(name);
|
|
115
|
-
loggers[name]
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
170
|
+
const logger = loggers[name];
|
|
171
|
+
logger.setLevel(logLevel);
|
|
172
|
+
logger.maskingPatterns = maskingPatternsConfig[name] ?? parseMaskingPatterns(process.env.WDIO_LOG_MASKING_PATTERNS);
|
|
173
|
+
logger.progress = progress;
|
|
174
|
+
logger.methodFactory = wdioLoggerMethodFactory(logger);
|
|
175
|
+
prefix.apply(logger, {
|
|
119
176
|
template: "%t %l %n:",
|
|
120
177
|
timestampFormatter: (date) => chalk.gray(date.toISOString()),
|
|
121
178
|
levelFormatter: (level) => chalk[COLORS[level]](level.toUpperCase()),
|
|
122
179
|
nameFormatter: (name2) => chalk.whiteBright(name2)
|
|
123
180
|
});
|
|
124
|
-
return
|
|
181
|
+
return logger;
|
|
125
182
|
}
|
|
126
183
|
getLogger.waitForBuffer = async () => new Promise((resolve) => {
|
|
127
184
|
if (logFile && Array.isArray(logFile.writableBuffer) && logFile.writableBuffer.length !== 0) {
|
|
@@ -154,6 +211,24 @@ getLogger.setLogLevelsConfig = (logLevels = {}, wdioLogLevel = DEFAULT_LEVEL) =>
|
|
|
154
211
|
loggers[logName].setLevel(logLevel);
|
|
155
212
|
});
|
|
156
213
|
};
|
|
214
|
+
getLogger.setMaskingPatterns = (pattern) => {
|
|
215
|
+
if (typeof pattern === "string") {
|
|
216
|
+
if (process.env.WDIO_LOG_MASKING_PATTERNS === void 0) {
|
|
217
|
+
process.env.WDIO_LOG_MASKING_PATTERNS = pattern;
|
|
218
|
+
}
|
|
219
|
+
} else if (typeof pattern === "object") {
|
|
220
|
+
maskingPatternsConfig = Object.entries(pattern).reduce((acc, [logName, maskingPatternsString]) => {
|
|
221
|
+
acc[logName] = parseMaskingPatterns(maskingPatternsString);
|
|
222
|
+
return acc;
|
|
223
|
+
}, maskingPatternsConfig);
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error(`Invalid pattern property, expected \`string\` or \`Record<string, string>\` but received \`${typeof pattern}\``);
|
|
226
|
+
}
|
|
227
|
+
Object.keys(loggers).forEach((logName) => {
|
|
228
|
+
const maskingPatterns = maskingPatternsConfig[logName] ?? parseMaskingPatterns(process.env.WDIO_LOG_MASKING_PATTERNS);
|
|
229
|
+
loggers[logName].maskingPatterns = maskingPatterns;
|
|
230
|
+
});
|
|
231
|
+
};
|
|
157
232
|
var getLogLevelName = (logName) => logName.split(":").shift();
|
|
158
233
|
function logsContainInitPackageError(logText) {
|
|
159
234
|
return ERROR_LOG_VALIDATOR.every((pattern) => logText.includes(pattern));
|
|
@@ -165,5 +240,6 @@ var ERROR_LOG_VALIDATOR = [
|
|
|
165
240
|
"Please make sure you have it installed"
|
|
166
241
|
];
|
|
167
242
|
export {
|
|
243
|
+
SENSITIVE_DATA_REPLACER,
|
|
168
244
|
getLogger as default
|
|
169
245
|
};
|
package/build/utils.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const SENSITIVE_DATA_REPLACER = "**MASKED**";
|
|
2
|
+
/**
|
|
3
|
+
* Parses a comma-separated string of regular expressions into an array of RegExp objects.
|
|
4
|
+
* Supports both `/pattern/flags` and plain pattern formats.
|
|
5
|
+
*
|
|
6
|
+
* @param {string | undefined} maskingRegexString - The string containing regex patterns.
|
|
7
|
+
* @returns {(RegExp[] | undefined)} Array of RegExp objects or undefined if input is invalid.
|
|
8
|
+
*/
|
|
9
|
+
export declare const parseMaskingPatterns: (maskingRegexString: string | undefined) => any[] | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Masks sensitive data in a string using the provided masking patterns.
|
|
12
|
+
*
|
|
13
|
+
* - If a pattern has no capturing groups, the whole match is replaced with the mask.
|
|
14
|
+
* - If a pattern has capturing groups, each group is replaced with the mask, preserving the rest of the match.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} text - The text to mask.
|
|
17
|
+
* @param {RegExp[] | undefined} maskingPatterns - Array of RegExp patterns to use for masking.
|
|
18
|
+
* @returns {string} The masked text, or the original value if not a string or if no patterns are provided.
|
|
19
|
+
*/
|
|
20
|
+
export declare const mask: (text: string, maskingPatterns: RegExp[] | undefined) => string;
|
|
21
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,eAAe,CAAA;AAUnD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,uBAAwB,MAAM,GAAG,SAAS,sBAgB1E,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI,SAAU,MAAM,mBAAmB,MAAM,EAAE,GAAG,SAAS,WA6BvE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdio/logger",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.15.0",
|
|
4
4
|
"description": "A helper utility for logging of WebdriverIO packages",
|
|
5
5
|
"author": "Christian Bromann <mail@bromann.dev>",
|
|
6
6
|
"homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-logger",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "0161f7203a99a181ee945286b676b37e032763fe"
|
|
49
49
|
}
|