Skip to content

Commit 54d259c

Browse files
Add env variable to not redact in environments where its set to false
1 parent d324a80 commit 54d259c

4 files changed

Lines changed: 111 additions & 6 deletions

File tree

packages/server/shared/src/lib/logger/log-cleaner.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22

33
import { ApplicationError } from '@openops/shared';
4+
import { system } from '../system';
5+
import { SharedSystemProp } from '../system/system-prop';
46

57
export const maxFieldLength = 2048;
68

@@ -23,18 +25,26 @@ const SENSITIVE_FIELD_PATTERNS = SENSITIVE_PATTERNS.map(
2325
new RegExp(String.raw`"[^"]*${pattern}[^"]*"\s*:\s*"[^"]*"`, 'gi'),
2426
);
2527

28+
const shouldRedactLogs = (): boolean => {
29+
return system.getBoolean(SharedSystemProp.REDACT_LOGS) ?? true;
30+
};
31+
2632
const isSensitiveField = (key: string): boolean => {
33+
if (!shouldRedactLogs()) {
34+
return false;
35+
}
2736
const lowerKey = key.toLowerCase();
2837
return SENSITIVE_PATTERNS.some((pattern) => lowerKey.includes(pattern));
2938
};
3039

3140
const redactSensitiveFields = (obj: any, visited = new WeakSet()): any => {
3241
try {
33-
if (obj === null || obj === undefined) {
34-
return obj;
35-
}
36-
37-
if (typeof obj !== 'object') {
42+
if (
43+
obj === null ||
44+
obj === undefined ||
45+
typeof obj !== 'object' ||
46+
!shouldRedactLogs()
47+
) {
3848
return obj;
3949
}
4050

@@ -67,7 +77,7 @@ const redactSensitiveFields = (obj: any, visited = new WeakSet()): any => {
6777
const redactSensitiveDataInString = (
6878
value: string | undefined,
6979
): string | undefined => {
70-
if (!value) {
80+
if (!value || !shouldRedactLogs()) {
7181
return value;
7282
}
7383
let result = value;

packages/server/shared/src/lib/system/system-prop.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export enum AppSystemProp {
115115
export enum SharedSystemProp {
116116
LOG_LEVEL = 'LOG_LEVEL',
117117
LOG_PRETTY = 'LOG_PRETTY',
118+
REDACT_LOGS = 'REDACT_LOGS',
118119
ENVIRONMENT = 'ENVIRONMENT',
119120
CONTAINER_TYPE = 'CONTAINER_TYPE',
120121
TRIGGER_TIMEOUT_SECONDS = 'TRIGGER_TIMEOUT_SECONDS',

packages/server/shared/src/lib/system/system.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const systemPropDefaultValues: Partial<Record<SystemProp, string>> = {
5959
[WorkerSystemProps.SCHEDULED_WORKER_CONCURRENCY]: '10',
6060
[SharedSystemProp.LOG_LEVEL]: 'info',
6161
[SharedSystemProp.LOG_PRETTY]: 'false',
62+
[SharedSystemProp.REDACT_LOGS]: 'true',
6263
[SharedSystemProp.PACKAGE_ARCHIVE_PATH]: 'cache/archives',
6364
[SharedSystemProp.BLOCKS_SOURCE]: BlocksSource.FILE,
6465
[AppSystemProp.QUEUE_MODE]: QueueMode.REDIS,

packages/server/shared/test/log-cleaner.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,61 @@ describe('log-cleaner', () => {
135135
});
136136

137137
describe('sensitive data redaction', () => {
138+
const originalEnv = process.env['OPS_REDACT_LOGS'];
139+
140+
afterEach(() => {
141+
if (originalEnv === undefined) {
142+
delete process.env['OPS_REDACT_LOGS'];
143+
} else {
144+
process.env['OPS_REDACT_LOGS'] = originalEnv;
145+
}
146+
});
147+
148+
it('should redact password field when redaction is enabled', () => {
149+
process.env['OPS_REDACT_LOGS'] = 'true';
150+
const logEvent = {
151+
event: {
152+
email: 'user@example.com',
153+
password: 'secret',
154+
},
155+
};
156+
157+
const result = cleanLogEvent(logEvent);
158+
159+
expect(result.event.password).toBe('[REDACTED]');
160+
expect(result.event.email).toBe('user@example.com');
161+
});
162+
163+
it('should NOT redact password field when redaction is disabled', () => {
164+
process.env['OPS_REDACT_LOGS'] = 'false';
165+
const logEvent = {
166+
event: {
167+
email: 'user@example.com',
168+
password: 'secret',
169+
},
170+
};
171+
172+
const result = cleanLogEvent(logEvent);
173+
174+
expect(result.event.password).toBe('secret');
175+
expect(result.event.email).toBe('user@example.com');
176+
});
177+
178+
it('should redact password field by default when variable is not set', () => {
179+
delete process.env['OPS_REDACT_LOGS'];
180+
const logEvent = {
181+
event: {
182+
email: 'user@example.com',
183+
password: 'secret',
184+
},
185+
};
186+
187+
const result = cleanLogEvent(logEvent);
188+
189+
expect(result.event.password).toBe('[REDACTED]');
190+
expect(result.event.email).toBe('user@example.com');
191+
});
192+
138193
it('should redact password field', () => {
139194
const logEvent = {
140195
event: {
@@ -150,6 +205,7 @@ describe('log-cleaner', () => {
150205
});
151206

152207
it('should redact authorization field in objects', () => {
208+
process.env['OPS_REDACT_LOGS'] = 'true';
153209
const logEvent = {
154210
event: {
155211
request: {
@@ -166,6 +222,7 @@ describe('log-cleaner', () => {
166222
});
167223

168224
it('should redact password in stringified JSON', () => {
225+
process.env['OPS_REDACT_LOGS'] = 'true';
169226
const logEvent = {
170227
event: {
171228
body: '{"email":"user@example.com","password":"secret"}',
@@ -179,6 +236,7 @@ describe('log-cleaner', () => {
179236
});
180237

181238
it('should redact password in nested objects', () => {
239+
process.env['OPS_REDACT_LOGS'] = 'true';
182240
const logEvent = {
183241
event: {
184242
request: {
@@ -193,6 +251,41 @@ describe('log-cleaner', () => {
193251
expect(result.event.request).toContain('[REDACTED]');
194252
expect(result.event.request).not.toContain('secret');
195253
});
254+
255+
it('should NOT redact token usage statistics when redaction is disabled', () => {
256+
process.env['OPS_REDACT_LOGS'] = 'false';
257+
const logEvent = {
258+
message: 'Total token usage for stream',
259+
event: {
260+
usage:
261+
'{"inputTokens":"1234","outputTokens":"5678","totalTokens":"6912","cachedInputTokens":"100"}',
262+
},
263+
};
264+
265+
const result = cleanLogEvent(logEvent);
266+
267+
expect(result.event.usage).toContain('1234');
268+
expect(result.event.usage).toContain('5678');
269+
expect(result.event.usage).toContain('6912');
270+
expect(result.event.usage).toContain('100');
271+
expect(result.event.usage).not.toContain('[REDACTED]');
272+
});
273+
274+
it('should redact token usage statistics when redaction is enabled', () => {
275+
process.env['OPS_REDACT_LOGS'] = 'true';
276+
const logEvent = {
277+
message: 'Total token usage for stream',
278+
event: {
279+
usage:
280+
'{"inputTokens":"1234","outputTokens":"5678","totalTokens":"6912","cachedInputTokens":"100"}',
281+
},
282+
};
283+
284+
const result = cleanLogEvent(logEvent);
285+
286+
expect(result.event.usage).toContain('[REDACTED]');
287+
expect(result.event.usage).not.toContain('1234');
288+
});
196289
});
197290

198291
describe('error objects', () => {

0 commit comments

Comments
 (0)