Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(tools): delegate copilot env reference resolution to the sha…
…red executor resolver

Replaces the hand-rolled exact-match regex with resolveEnvVarReferences
(allowEmbedded: false), the same resolver used by workflow runs, MCP config,
and webhooks — one set of reference semantics instead of two that can drift.
Behavior is identical; all existing tests pass unchanged.
  • Loading branch information
waleedlatif1 committed Jul 14, 2026
commit b6f0314b50c23a68ac2f58bdbd7a966690140068
39 changes: 18 additions & 21 deletions apps/sim/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { assertPermissionsAllowed } from '@/ee/access-control/utils/permission-c
import { isCustomTool, isMcpTool } from '@/executor/constants'
import { resolveSkillContent } from '@/executor/handlers/agent/skills-resolver'
import type { ExecutionContext, UserFile } from '@/executor/types'
import { resolveEnvVarReferences } from '@/executor/utils/reference-validation'
import type { ErrorInfo } from '@/tools/error-extractors'
import { extractErrorMessage } from '@/tools/error-extractors'
import type {
Expand Down Expand Up @@ -183,22 +184,16 @@ async function normalizeCopilotFileParams(
}
}

/**
* Matches a string that is exactly one {{ENV_VAR}} reference (same charset the
* executor's exact-match resolver accepts). Embedded references are
* intentionally not matched.
*/
const COPILOT_ENV_REFERENCE_PATTERN = /^\{\{([^}]+)\}\}$/

/**
* Resolves whole-value {{ENV_VAR}} references in user-only params for copilot
* tool executions. Chat agents never see secret values (the workspace VFS
* exposes env var names only), so they pass references; workflow runs resolve
* these in the executor, and this is the equivalent step for direct tool
* calls. Resolution is deliberately restricted to params declared
* `visibility: 'user-only'` (API keys and other operator-supplied secrets)
* and to values that are exactly one reference, so LLM-writable params (URLs,
* headers, bodies) can never be used to extract secret values.
* calls, delegating to the executor's resolver so both paths share one set of
* reference semantics. Resolution is deliberately restricted to params
* declared `visibility: 'user-only'` (API keys and other operator-supplied
* secrets) and to values that are exactly one reference, so LLM-writable
* params (URLs, headers, bodies) can never be used to extract secret values.
*
* Mutates only the given params object — callers pass the per-execution copy,
* never the copilot-side tool-call state, so decrypted values cannot leak
Expand All @@ -213,14 +208,12 @@ async function resolveCopilotEnvReferences(
return
}

const pending: Array<{ paramId: string; envKey: string }> = []
const pending: Array<{ paramId: string; value: string }> = []
for (const [paramId, paramDef] of Object.entries(tool.params || {})) {
if (paramDef?.visibility !== 'user-only') continue
const value = params[paramId]
if (typeof value !== 'string') continue
const match = COPILOT_ENV_REFERENCE_PATTERN.exec(value)
if (match) {
pending.push({ paramId, envKey: match[1].trim() })
if (typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}')) {
pending.push({ paramId, value })
}
}

Expand All @@ -231,15 +224,19 @@ async function resolveCopilotEnvReferences(
const { getEffectiveDecryptedEnv } = await import('@/lib/environment/utils')
const envVars = await getEffectiveDecryptedEnv(scope.userId, scope.workspaceId)
Comment thread
waleedlatif1 marked this conversation as resolved.

for (const { paramId, envKey } of pending) {
const resolved = envVars[envKey]
if (resolved === undefined) {
for (const { paramId, value } of pending) {
const missingKeys: string[] = []
const resolved = resolveEnvVarReferences(value, envVars, {
allowEmbedded: false,
missingKeys,
})
if (missingKeys.length > 0) {
throw new Error(
`Environment variable "${envKey}" referenced by parameter "${paramId}" was not found. ` +
`Environment variable "${missingKeys[0]}" referenced by parameter "${paramId}" was not found. ` +
`Check environment/variables.json for available variable names.`
)
}
params[paramId] = resolved
params[paramId] = resolved as string
}
}

Expand Down
Loading