Redact settings and remove old ones

This commit is contained in:
Andras Schmelczer 2026-09-19 22:16:52 +01:00
commit 7fe7ad27aa
2 changed files with 31 additions and 9 deletions

View file

@ -0,0 +1,23 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { Logger, LogLevel } from "../tracing/logger";
import { Settings } from "./settings";
describe("Settings logging", () => {
it("redacts tokens while loading and updating settings", async () => {
const logger = new Logger();
const settings = new Settings(
logger,
{ token: "loaded-secret" },
async () => {}
);
await settings.setSettings({ token: "updated-secret" });
const messages = logger
.getMessages(LogLevel.DEBUG)
.map((entry) => entry.message)
.join("\n");
assert.doesNotMatch(messages, /loaded-secret|updated-secret/u);
assert.match(messages, /\[REDACTED\]/u);
});
});

View file

@ -6,36 +6,35 @@ export interface SyncSettings {
remoteUri: string; remoteUri: string;
token: string; token: string;
vaultName: string; vaultName: string;
syncConcurrency: number;
isSyncEnabled: boolean; isSyncEnabled: boolean;
maxFileSizeMB: number; maxFileSizeMB: number;
ignorePatterns: string[]; ignorePatterns: string[];
webSocketRetryIntervalMs: number; webSocketRetryIntervalMs: number;
diffCacheSizeMB: number;
enableTelemetry: boolean; enableTelemetry: boolean;
requestTimeoutMs: number; requestTimeoutMs: number;
networkRetryIntervalMs: number; networkRetryIntervalMs: number;
syncIntervalMs?: number; syncIntervalMs?: number;
minimumSaveIntervalMs: number;
} }
export const DEFAULT_SETTINGS: SyncSettings = { export const DEFAULT_SETTINGS: SyncSettings = {
remoteUri: "", remoteUri: "",
token: "", token: "",
vaultName: "default", vaultName: "default",
syncConcurrency: 1,
isSyncEnabled: false, isSyncEnabled: false,
maxFileSizeMB: 10, maxFileSizeMB: 10,
ignorePatterns: [], ignorePatterns: [],
webSocketRetryIntervalMs: 3500, webSocketRetryIntervalMs: 3500,
diffCacheSizeMB: 4,
enableTelemetry: false, enableTelemetry: false,
requestTimeoutMs: 30_000, requestTimeoutMs: 30_000,
networkRetryIntervalMs: 1000, networkRetryIntervalMs: 1000,
syncIntervalMs: undefined, syncIntervalMs: undefined
minimumSaveIntervalMs: 1000
}; };
const redactSettings = (value: Partial<SyncSettings>): object => ({
...value,
...(value.token === undefined ? {} : { token: "[REDACTED]" })
});
export class Settings { export class Settings {
public readonly onSettingsChanged = new EventListeners< public readonly onSettingsChanged = new EventListeners<
(newSettings: SyncSettings, oldSettings: SyncSettings) => unknown (newSettings: SyncSettings, oldSettings: SyncSettings) => unknown
@ -55,7 +54,7 @@ export class Settings {
}; };
this.logger.debug( this.logger.debug(
`Loaded settings: ${JSON.stringify(this.settings, null, 2)}` `Loaded settings: ${JSON.stringify(redactSettings(this.settings), null, 2)}`
); );
} }
@ -78,7 +77,7 @@ export class Settings {
public async setSettings(value: Partial<SyncSettings>): Promise<void> { public async setSettings(value: Partial<SyncSettings>): Promise<void> {
await this.lock.withLock(async () => { await this.lock.withLock(async () => {
this.logger.debug( this.logger.debug(
`Updating settings with: ${JSON.stringify(value)}` `Updating settings with: ${JSON.stringify(redactSettings(value))}`
); );
const oldSettings = this.settings; const oldSettings = this.settings;
const next = { ...this.settings, ...value }; const next = { ...this.settings, ...value };