Skip to content

Commit a5ca0f4

Browse files
fix(logs): redact oversized strings and executionData.environment
- Drop the per-string size cap in PII redaction: oversized strings were left unmasked (leak). Nothing is skipped now; large payloads still fail-safe via the total-bytes ceiling + per-chunk timeout (scrub, never leak). - Add executionData.environment (incl. variables) to the redaction set.
1 parent e5dfe05 commit a5ca0f4

3 files changed

Lines changed: 21 additions & 15 deletions

File tree

apps/sim/lib/logs/execution/logger.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,9 @@ export class ExecutionLogger implements IExecutionLoggerService {
767767
...(builtExecutionData.executionState !== undefined
768768
? { executionState: builtExecutionData.executionState }
769769
: {}),
770+
...(builtExecutionData.environment !== undefined
771+
? { environment: builtExecutionData.environment }
772+
: {}),
770773
})
771774

772775
const rawDurationMs =
@@ -791,6 +794,9 @@ export class ExecutionLogger implements IExecutionLoggerService {
791794
...(pii.executionState !== undefined
792795
? { executionState: pii.executionState as SerializableExecutionState }
793796
: {}),
797+
...(pii.environment !== undefined
798+
? { environment: pii.environment as ExecutionEnvironment }
799+
: {}),
794800
}
795801

796802
stripSpanCosts((cleanExecutionData as Record<string, unknown>).traceSpans)

apps/sim/lib/logs/execution/pii-redaction.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,25 @@ describe('redactPIIFromExecution', () => {
7474
expect(result.finalOutput).toBe(REDACTION_FAILED_MARKER)
7575
})
7676

77-
it('skips oversized strings without consuming a masked slot', async () => {
78-
const huge = 'x'.repeat(200 * 1024)
79-
const payload = { finalOutput: { big: huge, small: 'pii' } }
77+
it('masks large strings too (never left unredacted)', async () => {
78+
const big = 'x'.repeat(200 * 1024)
79+
const payload = { finalOutput: { big, small: 'pii' } }
8080

8181
const result = await redactPIIFromExecution(payload, { entityTypes: [] })
8282

83-
expect((result.finalOutput as any).big).toBe(huge)
83+
expect((result.finalOutput as any).big).toBe(`MASKED(${big})`)
8484
expect((result.finalOutput as any).small).toBe('MASKED(pii)')
85-
expect(mockMaskPIIBatch.mock.calls[0][0]).toEqual(['pii'])
85+
expect(mockMaskPIIBatch.mock.calls[0][0]).toEqual([big, 'pii'])
8686
})
8787

88-
it('masks span error/errorMessage and top-level error, trigger, executionState', async () => {
88+
it('masks span error/errorMessage and top-level error, trigger, executionState, environment', async () => {
8989
const payload = {
9090
traceSpans: [{ blockId: 'b1', error: 'failed for bob@x.com', errorMessage: 'bad input z' }],
9191
error: 'run failed: a@b.com',
9292
completionFailure: 'cancelled by c@d.com',
9393
trigger: { type: 'webhook', data: { from: 'caller@x.com' } },
9494
executionState: { status: 'completed', note: 'state for e@f.com' },
95+
environment: { variables: { CONTACT: 'admin@x.com' } },
9596
}
9697

9798
const result = await redactPIIFromExecution(payload, { entityTypes: ['EMAIL_ADDRESS'] })
@@ -105,6 +106,7 @@ describe('redactPIIFromExecution', () => {
105106
expect((result.trigger as any).type).toBe('MASKED(webhook)')
106107
expect((result.trigger as any).data.from).toBe('MASKED(caller@x.com)')
107108
expect((result.executionState as any).note).toBe('MASKED(state for e@f.com)')
109+
expect((result.environment as any).variables.CONTACT).toBe('MASKED(admin@x.com)')
108110
})
109111

110112
it('returns payload unchanged when there is nothing to mask', async () => {

apps/sim/lib/logs/execution/pii-redaction.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ const logger = createLogger('PiiRedaction')
77
export const REDACTION_FAILED_MARKER = '[REDACTION_FAILED]'
88

99
/**
10-
* Strings larger than this are skipped (left as-is). They are almost always
11-
* base64 blobs / embedded JSON rather than PII prose, and would dominate NER time.
12-
*/
13-
const PII_MAX_STRING_BYTES = 128 * 1024
14-
15-
/**
16-
* Upper bound on total text masked for one execution. Beyond this we scrub rather
17-
* than spend minutes in NER. Typical inline logs (≤3MB) stay well under.
10+
* Upper bound on total text masked for one execution. Beyond this we scrub the
11+
* whole payload rather than spend minutes in NER (never leave it unmasked).
12+
* Typical inline logs (≤3MB) stay well under. Individual strings are never
13+
* skipped by size — they would otherwise persist unredacted.
1814
*/
1915
const PII_MAX_TOTAL_BYTES = 16 * 1024 * 1024
2016

@@ -32,6 +28,7 @@ export interface RedactablePayload {
3228
completionFailure?: unknown
3329
trigger?: unknown
3430
executionState?: unknown
31+
environment?: unknown
3532
}
3633

3734
/** Keys of {@link RedactablePayload} processed by the redactor, in order. */
@@ -43,6 +40,7 @@ const REDACTABLE_KEYS: (keyof RedactablePayload)[] = [
4340
'completionFailure',
4441
'trigger',
4542
'executionState',
43+
'environment',
4644
]
4745

4846
/** Trace-span fields that carry runtime content (and therefore possible PII). */
@@ -57,7 +55,7 @@ const SPAN_CONTENT_FIELDS = [
5755
] as const
5856

5957
function isEligibleString(value: string): boolean {
60-
return value.length > 0 && Buffer.byteLength(value, 'utf8') <= PII_MAX_STRING_BYTES
58+
return value.length > 0
6159
}
6260

6361
/**

0 commit comments

Comments
 (0)