|
1 | 1 | import "server-only"; |
2 | 2 |
|
3 | 3 | import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto"; |
4 | | -import { and, eq } from "drizzle-orm"; |
| 4 | +import { and, eq, inArray } from "drizzle-orm"; |
5 | 5 | import type { IntegrationConfig, IntegrationType } from "../types/integration"; |
6 | 6 | import { db } from "./index"; |
7 | 7 | import { integrations, type NewIntegration } from "./schema"; |
@@ -260,3 +260,70 @@ export async function deleteIntegration( |
260 | 260 |
|
261 | 261 | return result.length > 0; |
262 | 262 | } |
| 263 | + |
| 264 | +/** |
| 265 | + * Workflow node structure for validation |
| 266 | + */ |
| 267 | +type WorkflowNodeForValidation = { |
| 268 | + data?: { |
| 269 | + config?: { |
| 270 | + integrationId?: string; |
| 271 | + }; |
| 272 | + }; |
| 273 | +}; |
| 274 | + |
| 275 | +/** |
| 276 | + * Extract all integration IDs from workflow nodes |
| 277 | + */ |
| 278 | +export function extractIntegrationIds( |
| 279 | + nodes: WorkflowNodeForValidation[] |
| 280 | +): string[] { |
| 281 | + const integrationIds: string[] = []; |
| 282 | + |
| 283 | + for (const node of nodes) { |
| 284 | + const integrationId = node.data?.config?.integrationId; |
| 285 | + if (integrationId && typeof integrationId === "string") { |
| 286 | + integrationIds.push(integrationId); |
| 287 | + } |
| 288 | + } |
| 289 | + |
| 290 | + return [...new Set(integrationIds)]; |
| 291 | +} |
| 292 | + |
| 293 | +/** |
| 294 | + * Validate that all integration IDs in workflow nodes belong to the specified user. |
| 295 | + * This prevents users from accessing other users' credentials by embedding |
| 296 | + * foreign integration IDs in their workflows. |
| 297 | + * |
| 298 | + * @returns Object with `valid` boolean and optional `invalidIds` array |
| 299 | + */ |
| 300 | +export async function validateWorkflowIntegrations( |
| 301 | + nodes: WorkflowNodeForValidation[], |
| 302 | + userId: string |
| 303 | +): Promise<{ valid: boolean; invalidIds?: string[] }> { |
| 304 | + const integrationIds = extractIntegrationIds(nodes); |
| 305 | + |
| 306 | + if (integrationIds.length === 0) { |
| 307 | + return { valid: true }; |
| 308 | + } |
| 309 | + |
| 310 | + // Query for integrations that belong to this user |
| 311 | + const userIntegrations = await db |
| 312 | + .select({ id: integrations.id }) |
| 313 | + .from(integrations) |
| 314 | + .where( |
| 315 | + and( |
| 316 | + inArray(integrations.id, integrationIds), |
| 317 | + eq(integrations.userId, userId) |
| 318 | + ) |
| 319 | + ); |
| 320 | + |
| 321 | + const validIds = new Set(userIntegrations.map((i) => i.id)); |
| 322 | + const invalidIds = integrationIds.filter((id) => !validIds.has(id)); |
| 323 | + |
| 324 | + if (invalidIds.length > 0) { |
| 325 | + return { valid: false, invalidIds }; |
| 326 | + } |
| 327 | + |
| 328 | + return { valid: true }; |
| 329 | +} |
0 commit comments