From 1803d23786e7f0c30f63be50bed7632f0ca49a52 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 12 Dec 2025 17:39:13 -0800 Subject: [PATCH 1/4] Fix --- .../lib/workflows/sanitization/json-sanitizer.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts index 99477ff036..7c77a372c0 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts @@ -239,20 +239,6 @@ function sanitizeSubBlocks( return } - // For sensitive fields, either omit or replace with placeholder - if (isSensitiveSubBlock(key, subBlock)) { - // If it's an environment variable reference, keep it - if ( - typeof subBlock.value === 'string' && - subBlock.value.startsWith('{{') && - subBlock.value.endsWith('}}') - ) { - sanitized[key] = subBlock.value - } - // Otherwise omit the sensitive value entirely - return - } - // Special handling for condition-input type - clean UI metadata if (subBlock.type === 'condition-input' && typeof subBlock.value === 'string') { const cleanedConditions: string = sanitizeConditions(subBlock.value) From 89cbb3bbccfd31186c203a1f47bcfc366fdaa49e Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 12 Dec 2025 17:45:37 -0800 Subject: [PATCH 2/4] Fix --- .../workflows/sanitization/json-sanitizer.ts | 72 ++++++++----------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts index 7c77a372c0..ad9e2a22ce 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts @@ -171,9 +171,26 @@ function sanitizeTools(tools: any[]): any[] { } /** - * Sanitize subblocks by removing null values, secrets, and simplifying structure + * Sort object keys recursively for consistent comparison + */ +function sortKeysRecursively(item: any): any { + if (Array.isArray(item)) { + return item.map(sortKeysRecursively) + } + if (item !== null && typeof item === 'object') { + return Object.keys(item) + .sort() + .reduce((result: any, key: string) => { + result[key] = sortKeysRecursively(item[key]) + return result + }, {}) + } + return item +} + +/** + * Sanitize subblocks by removing null values and simplifying structure * Maps each subblock key directly to its value instead of the full object - * Note: responseFormat is kept as an object for better copilot understanding */ function sanitizeSubBlocks( subBlocks: BlockState['subBlocks'] @@ -181,64 +198,37 @@ function sanitizeSubBlocks( const sanitized: Record = {} Object.entries(subBlocks).forEach(([key, subBlock]) => { - // Special handling for responseFormat - process BEFORE null check - // so we can detect when it's added/removed + // Skip null/undefined values + if (subBlock.value === null || subBlock.value === undefined) { + return + } + + // Normalize responseFormat for consistent key ordering (important for training data) if (key === 'responseFormat') { try { - // Handle null/undefined - skip if no value - if (subBlock.value === null || subBlock.value === undefined) { - return - } - let obj = subBlock.value - // Handle string values - parse them first + // Parse JSON string if needed if (typeof subBlock.value === 'string') { const trimmed = subBlock.value.trim() if (!trimmed) { - // Empty string - skip this field return } obj = JSON.parse(trimmed) } - // Handle object values - normalize keys and keep as object for copilot + // Sort keys for consistent comparison if (obj && typeof obj === 'object') { - // Sort keys recursively for consistent comparison - const sortKeys = (item: any): any => { - if (Array.isArray(item)) { - return item.map(sortKeys) - } - if (item !== null && typeof item === 'object') { - return Object.keys(item) - .sort() - .reduce((result: any, key: string) => { - result[key] = sortKeys(item[key]) - return result - }, {}) - } - return item - } - - // Keep as object (not stringified) for better copilot understanding - const normalized = sortKeys(obj) - sanitized[key] = normalized + sanitized[key] = sortKeysRecursively(obj) return } - - // If we get here, obj is not an object (maybe null or primitive) - skip it - return - } catch (error) { - // Invalid JSON - skip this field to avoid crashes + } catch { + // Invalid JSON - pass through as-is + sanitized[key] = subBlock.value return } } - // Skip null/undefined values for other fields - if (subBlock.value === null || subBlock.value === undefined) { - return - } - // Special handling for condition-input type - clean UI metadata if (subBlock.type === 'condition-input' && typeof subBlock.value === 'string') { const cleanedConditions: string = sanitizeConditions(subBlock.value) From 53b1d8dd97366c61f29e114f19c883a717dd20f3 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 12 Dec 2025 17:51:05 -0800 Subject: [PATCH 3/4] Remove dead code --- .../workflows/sanitization/json-sanitizer.ts | 42 ++----------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts index ad9e2a22ce..dc8807b176 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts @@ -2,7 +2,6 @@ import type { Edge } from 'reactflow' import { sanitizeWorkflowForSharing } from '@/lib/workflows/credentials/credential-extractor' import type { BlockState, Loop, Parallel, WorkflowState } from '@/stores/workflows/workflow/types' import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' -import { TRIGGER_PERSISTED_SUBBLOCK_IDS } from '@/triggers/constants' /** * Sanitized workflow state for copilot (removes all UI-specific data) @@ -65,41 +64,6 @@ export interface ExportWorkflowState { } } -/** - * Check if a subblock contains sensitive/secret data - */ -function isSensitiveSubBlock(key: string, subBlock: BlockState['subBlocks'][string]): boolean { - if (TRIGGER_PERSISTED_SUBBLOCK_IDS.includes(key)) { - return false - } - - // Check if it's an OAuth input type - if (subBlock.type === 'oauth-input') { - return true - } - - // Check if the field name suggests it contains sensitive data - const sensitivePattern = /credential|oauth|api[_-]?key|token|secret|auth|password|bearer/i - if (sensitivePattern.test(key)) { - return true - } - - // Check if the value itself looks like a secret (but not environment variable references) - if (typeof subBlock.value === 'string' && subBlock.value.length > 0) { - // Don't sanitize environment variable references like {{VAR_NAME}} - if (subBlock.value.startsWith('{{') && subBlock.value.endsWith('}}')) { - return false - } - - // If it matches sensitive patterns in the value, it's likely a hardcoded secret - if (sensitivePattern.test(subBlock.value)) { - return true - } - } - - return false -} - /** * Sanitize condition blocks by removing UI-specific metadata * Returns cleaned JSON string (not parsed array) @@ -199,9 +163,9 @@ function sanitizeSubBlocks( Object.entries(subBlocks).forEach(([key, subBlock]) => { // Skip null/undefined values - if (subBlock.value === null || subBlock.value === undefined) { - return - } + if (subBlock.value === null || subBlock.value === undefined) { + return + } // Normalize responseFormat for consistent key ordering (important for training data) if (key === 'responseFormat') { From e754f207da54019461aae8d04554a6383f3e5164 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 12 Dec 2025 17:52:52 -0800 Subject: [PATCH 4/4] Fix lint --- apps/sim/lib/workflows/sanitization/json-sanitizer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts index dc8807b176..eb062599f0 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts @@ -163,9 +163,9 @@ function sanitizeSubBlocks( Object.entries(subBlocks).forEach(([key, subBlock]) => { // Skip null/undefined values - if (subBlock.value === null || subBlock.value === undefined) { - return - } + if (subBlock.value === null || subBlock.value === undefined) { + return + } // Normalize responseFormat for consistent key ordering (important for training data) if (key === 'responseFormat') {