diff --git a/src/logging/BaseLogger.ts b/src/logging/BaseLogger.ts index 723c2f17..e32b9f0f 100644 --- a/src/logging/BaseLogger.ts +++ b/src/logging/BaseLogger.ts @@ -17,15 +17,17 @@ limitations under the License. import {LogItem} from "./LogItem"; import {LogLevel, LogFilter} from "./LogFilter"; -import type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem} from "./types"; +import type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem, ISerializedItem} from "./types"; import type {Platform} from "../platform/web/Platform.js"; export abstract class BaseLogger implements ILogger { protected _openItems: Set = new Set(); protected _platform: Platform; + protected _serializedTransformer: (item: ISerializedItem) => ISerializedItem; - constructor({platform}) { + constructor({platform, serializedTransformer = (item: ISerializedItem) => item}) { this._platform = platform; + this._serializedTransformer = serializedTransformer; } log(labelOrValues: LabelOrValues, logLevel: LogLevel = LogLevel.Info): void { diff --git a/src/logging/IDBLogger.ts b/src/logging/IDBLogger.ts index 96147ca1..ab9474b0 100644 --- a/src/logging/IDBLogger.ts +++ b/src/logging/IDBLogger.ts @@ -26,7 +26,7 @@ import {BaseLogger} from "./BaseLogger"; import type {Interval} from "../platform/web/dom/Clock"; import type {Platform} from "../platform/web/Platform.js"; import type {BlobHandle} from "../platform/web/dom/BlobHandle.js"; -import type {ILogItem, ILogExport} from "./types"; +import type {ILogItem, ILogExport, ISerializedItem} from "./types"; import type {LogFilter} from "./LogFilter"; type QueuedItem = { @@ -40,7 +40,7 @@ export class IDBLogger extends BaseLogger { private readonly _flushInterval: Interval; private _queuedItems: QueuedItem[]; - constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform}) { + constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform, serializedTransformer?: (item: ISerializedItem) => ISerializedItem}) { super(options); const {name, flushInterval = 60 * 1000, limit = 3000} = options; this._name = name; @@ -119,9 +119,12 @@ export class IDBLogger extends BaseLogger { _persistItem(logItem: ILogItem, filter: LogFilter, forced: boolean): void { const serializedItem = logItem.serialize(filter, undefined, forced); - this._queuedItems.push({ - json: JSON.stringify(serializedItem) - }); + if (serializedItem) { + const transformedSerializedItem = this._serializedTransformer(serializedItem); + this._queuedItems.push({ + json: JSON.stringify(transformedSerializedItem) + }); + } } _persistQueuedItems(items: QueuedItem[]): void { diff --git a/src/platform/web/Platform.js b/src/platform/web/Platform.js index f6cd2895..c871bb9e 100644 --- a/src/platform/web/Platform.js +++ b/src/platform/web/Platform.js @@ -132,11 +132,7 @@ export class Platform { this.clock = new Clock(); this.encoding = new Encoding(); this.random = Math.random; - if (options?.development) { - this.logger = new ConsoleLogger({platform: this}); - } else { - this.logger = new IDBLogger({name: "hydrogen_logs", platform: this}); - } + this._createLogger(options?.development); this.history = new History(); this.onlineStatus = new OnlineStatus(); this._serviceWorkerHandler = null; @@ -162,6 +158,21 @@ export class Platform { this._disposables = new Disposables(); } + _createLogger(isDevelopment) { + // Make sure that loginToken does not end up in the logs + const transformer = (item) => { + if (item.e?.stack) { + item.e.stack = item.e.stack.replace(/(?<=\/\?loginToken=).+/, ""); + } + return item; + }; + if (isDevelopment) { + this.logger = new ConsoleLogger({platform: this}); + } else { + this.logger = new IDBLogger({name: "hydrogen_logs", platform: this, serializedTransformer: transformer}); + } + } + get updateService() { return this._serviceWorkerHandler; } @@ -272,3 +283,30 @@ export class Platform { this._disposables.dispose(); } } + +import {LogItem} from "../../logging/LogItem"; +export function tests() { + return { + "loginToken should not be in logs": (assert) => { + const transformer = (item) => { + if (item.e?.stack) { + item.e.stack = item.e.stack.replace(/(?<=\/\?loginToken=).+/, ""); + } + return item; + }; + const logger = { + _queuedItems: [], + _serializedTransformer: transformer, + _now: () => {} + }; + logger.persist = IDBLogger.prototype._persistItem.bind(logger); + const logItem = new LogItem("test", 1, logger); + logItem.error = new Error(); + logItem.error.stack = "main http://localhost:3000/src/main.js:55\n http://localhost:3000/?loginToken=secret:26" + logger.persist(logItem, null, false); + const item = logger._queuedItems.pop(); + console.log(item); + assert.strictEqual(item.json.search("secret"), -1); + } + }; +}