fix(settings): harden settings and diagnostic controls

This commit is contained in:
NimBold
2026-07-17 00:39:26 +03:30
parent 5144ecd39e
commit 6ef911919d
10 changed files with 226 additions and 18 deletions
+24
View File
@@ -5,6 +5,7 @@ import {
mergeLogSnapshotAndLiveEntries,
persistedLogEntry,
pushBoundedLogEntry,
redactLogText,
type LogEntry
} from './logEntries';
@@ -30,6 +31,29 @@ describe('log entry streaming', () => {
});
});
it('redacts live secrets, signed URL components, and the home path', () => {
const message = 'Cookie: session=secret; https://example.com/file?token=signed https://example.com/file#fragment /Users/nima/Downloads/file';
const redacted = redactLogText(message, '/Users/nima');
expect(redacted).toBe('Cookie: [redacted]; https://example.com/file?[redacted] https://example.com/file#[redacted] <HOME>/Downloads/file');
expect(redacted).not.toContain('secret');
expect(redacted).not.toContain('signed');
expect(redacted).not.toContain('/Users/nima');
});
it('redacts live content before it is formatted for display', () => {
expect(liveLogEntry(3, 'Authorization: Bearer secret').message).toContain('Authorization: [redacted]');
expect(liveLogEntry(3, 'Authorization: Bearer secret').message).not.toContain('secret');
});
it('redacts persisted content and quoted credential fields', () => {
const persisted = persistedLogEntry('{"api_key":"json-secret","path":"/Users/nima/file"}', '/Users/nima');
expect(persisted.message).toContain('api_key');
expect(persisted.message).not.toContain('json-secret');
expect(persisted.message).not.toContain('/Users/nima');
});
it('merges only the ordered snapshot-to-stream overlap', () => {
expect(mergeLogSnapshotAndLiveEntries(
[entry('one'), entry('repeat'), entry('three')],
+43 -7
View File
@@ -19,24 +19,60 @@ const LIVE_LEVELS: Record<number, LogLevel> = {
const levelFromMessage = (message: string): LogLevel | undefined =>
LEVEL_NAMES.find(level => message.includes(`[${level.toUpperCase()}]`));
export const persistedLogEntry = (message: string): LogEntry => ({
export const persistedLogEntry = (message: string, homePath = ''): LogEntry => ({
level: levelFromMessage(message) || 'Debug',
message
message: redactLogText(message, homePath)
});
export const redactLogText = (message: string, homePath = ''): string => {
const normalizedHome = homePath.trim();
const normalizedHomeWithForwardSlashes = normalizedHome.replace(/\\/g, '/');
let redacted = message;
if (normalizedHome) {
redacted = redacted.split(normalizedHome).join('<HOME>');
}
if (normalizedHomeWithForwardSlashes && normalizedHomeWithForwardSlashes !== normalizedHome) {
redacted = redacted.split(normalizedHomeWithForwardSlashes).join('<HOME>');
}
redacted = redacted.replace(
/(["'])(authorization|proxy-authorization|cookie|set-cookie|password|token|secret|credential|pairing[-_ ]?token|api[-_ ]?key)(["'])(\s*[:=]\s*)["'][^"\r\n,;]*["']/gi,
'$1$2$3$4[redacted]'
);
redacted = redacted.replace(
/([A-Za-z][A-Za-z0-9+.-]*:\/\/)[^@\s/?#]+@/g,
'$1[redacted]@'
);
redacted = redacted.replace(
/([A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s?]+)\?[^\s]+/g,
'$1?[redacted]'
);
redacted = redacted.replace(
/([A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s?#]+)#\S+/g,
'$1#[redacted]'
);
return redacted.replace(
/(authorization|proxy-authorization|cookie|set-cookie|password|token|secret|credential|pairing[-_ ]?token|api[-_ ]?key)(\s*)([:=])(\s*)([^\r\n,;]+)/gi,
'$1$2$3$4[redacted]'
);
};
export const liveLogEntry = (
numericLevel: number,
message: string,
now: Date = new Date()
now: Date = new Date(),
homePath = ''
): LogEntry => {
const level = levelFromMessage(message) || LIVE_LEVELS[numericLevel] || 'Debug';
const alreadyFormatted = /^\[\d{4}-\d{2}-\d{2}\]\[\d{2}:\d{2}:\d{2}\]\[(TRACE|DEBUG|INFO|WARN|ERROR)\]/.test(message);
const redactedMessage = redactLogText(message, homePath);
const level = levelFromMessage(redactedMessage) || LIVE_LEVELS[numericLevel] || 'Debug';
const alreadyFormatted = /^\[\d{4}-\d{2}-\d{2}\]\[\d{2}:\d{2}:\d{2}\]\[(TRACE|DEBUG|INFO|WARN|ERROR)\]/.test(redactedMessage);
return {
level,
message: alreadyFormatted
? message
: `[${now.toISOString().replace('T', ' ').substring(0, 19)}] [${level.toUpperCase()}] ${message}`
? redactedMessage
: `[${now.toISOString().replace('T', ' ').substring(0, 19)}] [${level.toUpperCase()}] ${redactedMessage}`
};
};