mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-10 11:37:21 +00:00
fix(settings): harden settings and diagnostic controls
This commit is contained in:
@@ -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
@@ -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}`
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user