Files
sim/apps/sim/lib/copilot/tools/server/blocks/get-blocks-and-tools.ts
Waleed bbcef7ce5c feat(access-control): add ALLOWED_INTEGRATIONS env var for self-hosted block restrictions (#3238)
* feat(access-control): add ALLOWED_INTEGRATIONS env var for self-hosted block restrictions

* fix(tests): add getAllowedIntegrationsFromEnv mock to agent-handler tests

* fix(access-control): add auth to allowlist endpoint, fix loading state race, use accurate error message

* fix(access-control): remove auth from allowed-integrations endpoint to match models endpoint pattern

* fix(access-control): normalize blockType to lowercase before env allowlist check

* fix(access-control): expose merged allowedIntegrations on config to prevent bypass via direct access

* consolidate merging of allowed blocks so all callers have it by default

* normalize to lower case

* added tests

* added tests, normalize to lower case

* added safety incase userId is missing

* fix failing tests
2026-02-17 18:46:24 -08:00

72 lines
2.6 KiB
TypeScript

import { createLogger } from '@sim/logger'
import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool'
import { GetBlocksAndToolsInput, GetBlocksAndToolsResult } from '@/lib/copilot/tools/shared/schemas'
import { getAllowedIntegrationsFromEnv } from '@/lib/core/config/feature-flags'
import { registry as blockRegistry } from '@/blocks/registry'
import type { BlockConfig } from '@/blocks/types'
import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
export const getBlocksAndToolsServerTool: BaseServerTool<
ReturnType<typeof GetBlocksAndToolsInput.parse>,
ReturnType<typeof GetBlocksAndToolsResult.parse>
> = {
name: 'get_blocks_and_tools',
inputSchema: GetBlocksAndToolsInput,
outputSchema: GetBlocksAndToolsResult,
async execute(_args: unknown, context?: { userId: string }) {
const logger = createLogger('GetBlocksAndToolsServerTool')
logger.debug('Executing get_blocks_and_tools')
const permissionConfig = context?.userId ? await getUserPermissionConfig(context.userId) : null
const allowedIntegrations =
permissionConfig?.allowedIntegrations ?? getAllowedIntegrationsFromEnv()
type BlockListItem = {
type: string
name: string
description?: string
triggerAllowed?: boolean
}
const blocks: BlockListItem[] = []
Object.entries(blockRegistry)
.filter(([blockType, blockConfig]: [string, BlockConfig]) => {
if (blockConfig.hideFromToolbar) return false
if (allowedIntegrations != null && !allowedIntegrations.includes(blockType.toLowerCase()))
return false
return true
})
.forEach(([blockType, blockConfig]: [string, BlockConfig]) => {
blocks.push({
type: blockType,
name: blockConfig.name,
description: blockConfig.longDescription,
triggerAllowed: 'triggerAllowed' in blockConfig ? !!blockConfig.triggerAllowed : false,
})
})
const specialBlocks: Record<string, { name: string; description: string }> = {
loop: {
name: 'Loop',
description:
'Control flow block for iterating over collections or repeating actions in a loop',
},
parallel: {
name: 'Parallel',
description: 'Control flow block for executing multiple branches simultaneously',
},
}
Object.entries(specialBlocks).forEach(([blockType, info]) => {
if (!blocks.some((b) => b.type === blockType)) {
blocks.push({
type: blockType,
name: info.name,
description: info.description,
})
}
})
return GetBlocksAndToolsResult.parse({ blocks })
},
}