diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b67ff38f56..7413dc33272 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: # Build AMD64 images and push to ECR immediately (+ GHCR for main) build-amd64: name: Build AMD64 - needs: [test-build, detect-version] + needs: [detect-version] if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/dev') runs-on: blacksmith-8vcpu-ubuntu-2404 permissions: @@ -70,7 +70,7 @@ jobs: ecr_repo_secret: ECR_REALTIME steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 @@ -150,7 +150,7 @@ jobs: # Build ARM64 images for GHCR (main branch only, runs in parallel) build-ghcr-arm64: name: Build ARM64 (GHCR Only) - needs: [test-build, detect-version] + needs: [detect-version] runs-on: blacksmith-8vcpu-ubuntu-2404-arm if: github.event_name == 'push' && github.ref == 'refs/heads/main' permissions: @@ -169,7 +169,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Login to GHCR uses: docker/login-action@v3 @@ -264,10 +264,10 @@ jobs: outputs: docs_changed: ${{ steps.filter.outputs.docs }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 2 # Need at least 2 commits to detect changes - - uses: dorny/paths-filter@v3 + - uses: dorny/paths-filter@v4 id: filter with: filters: | @@ -294,7 +294,7 @@ jobs: contents: write steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/docs-embeddings.yml b/.github/workflows/docs-embeddings.yml index 3a3d89c0713..3e4de08e19f 100644 --- a/.github/workflows/docs-embeddings.yml +++ b/.github/workflows/docs-embeddings.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index de8c59c9da4..2eab817d009 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: staging token: ${{ secrets.GH_PAT }} @@ -115,7 +115,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: staging diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml index 8028c433638..853ebc6881a 100644 --- a/.github/workflows/images.yml +++ b/.github/workflows/images.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 @@ -117,7 +117,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Login to GHCR uses: docker/login-action@v3 diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index 8a3f543c172..db084926861 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 0a9bea31400..ceb124c8230 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -14,7 +14,7 @@ jobs: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/publish-python-sdk.yml b/.github/workflows/publish-python-sdk.yml index a44d5a34223..85d110b53dd 100644 --- a/.github/workflows/publish-python-sdk.yml +++ b/.github/workflows/publish-python-sdk.yml @@ -14,7 +14,7 @@ jobs: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v5 diff --git a/.github/workflows/publish-ts-sdk.yml b/.github/workflows/publish-ts-sdk.yml index e826d4395fa..1032ce7442a 100644 --- a/.github/workflows/publish-ts-sdk.yml +++ b/.github/workflows/publish-ts-sdk.yml @@ -14,7 +14,7 @@ jobs: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index b8fab8a77c4..eb55aced79d 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/apps/sim/components/emails/render.ts b/apps/sim/components/emails/render.ts index 761b89f3788..e495232ef5e 100644 --- a/apps/sim/components/emails/render.ts +++ b/apps/sim/components/emails/render.ts @@ -1,4 +1,4 @@ -import { render } from '@react-email/components' +import { render } from '@react-email/render' import { OnboardingFollowupEmail, OTPVerificationEmail, diff --git a/apps/sim/lib/billing/webhooks/invoices.ts b/apps/sim/lib/billing/webhooks/invoices.ts index b0558292ec3..635aa8314ac 100644 --- a/apps/sim/lib/billing/webhooks/invoices.ts +++ b/apps/sim/lib/billing/webhooks/invoices.ts @@ -1,4 +1,4 @@ -import { render } from '@react-email/components' +import { render } from '@react-email/render' import { db } from '@sim/db' import { member, diff --git a/apps/sim/tools/confluence/add_label.ts b/apps/sim/tools/confluence/add_label.ts index db931b3cf0d..2ba111f529d 100644 --- a/apps/sim/tools/confluence/add_label.ts +++ b/apps/sim/tools/confluence/add_label.ts @@ -34,6 +34,8 @@ export const confluenceAddLabelTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_blogpost.ts b/apps/sim/tools/confluence/create_blogpost.ts index b39e91b7f18..e67e0f0732b 100644 --- a/apps/sim/tools/confluence/create_blogpost.ts +++ b/apps/sim/tools/confluence/create_blogpost.ts @@ -44,6 +44,8 @@ export const confluenceCreateBlogPostTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_comment.ts b/apps/sim/tools/confluence/create_comment.ts index aa18a5c4a0b..b09c6ddb54d 100644 --- a/apps/sim/tools/confluence/create_comment.ts +++ b/apps/sim/tools/confluence/create_comment.ts @@ -31,6 +31,8 @@ export const confluenceCreateCommentTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_page.ts b/apps/sim/tools/confluence/create_page.ts index 7a4fec8a846..f4f70bc4afb 100644 --- a/apps/sim/tools/confluence/create_page.ts +++ b/apps/sim/tools/confluence/create_page.ts @@ -40,6 +40,8 @@ export const confluenceCreatePageTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_page_property.ts b/apps/sim/tools/confluence/create_page_property.ts index 36ebfb04a06..99fbe246fdc 100644 --- a/apps/sim/tools/confluence/create_page_property.ts +++ b/apps/sim/tools/confluence/create_page_property.ts @@ -38,6 +38,8 @@ export const confluenceCreatePagePropertyTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_space.ts b/apps/sim/tools/confluence/create_space.ts index f2d8b8a734a..4981c4adc25 100644 --- a/apps/sim/tools/confluence/create_space.ts +++ b/apps/sim/tools/confluence/create_space.ts @@ -39,6 +39,8 @@ export const confluenceCreateSpaceTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/create_space_property.ts b/apps/sim/tools/confluence/create_space_property.ts index c702f63539a..e6fee973b82 100644 --- a/apps/sim/tools/confluence/create_space_property.ts +++ b/apps/sim/tools/confluence/create_space_property.ts @@ -35,6 +35,8 @@ export const confluenceCreateSpacePropertyTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_attachment.ts b/apps/sim/tools/confluence/delete_attachment.ts index 37d2d093d80..38159ee4d6a 100644 --- a/apps/sim/tools/confluence/delete_attachment.ts +++ b/apps/sim/tools/confluence/delete_attachment.ts @@ -30,6 +30,8 @@ export const confluenceDeleteAttachmentTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_blogpost.ts b/apps/sim/tools/confluence/delete_blogpost.ts index c53562cf283..6d03a3f60e1 100644 --- a/apps/sim/tools/confluence/delete_blogpost.ts +++ b/apps/sim/tools/confluence/delete_blogpost.ts @@ -31,6 +31,8 @@ export const confluenceDeleteBlogPostTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_comment.ts b/apps/sim/tools/confluence/delete_comment.ts index 6564181dfe0..7cfb1694987 100644 --- a/apps/sim/tools/confluence/delete_comment.ts +++ b/apps/sim/tools/confluence/delete_comment.ts @@ -30,6 +30,8 @@ export const confluenceDeleteCommentTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_label.ts b/apps/sim/tools/confluence/delete_label.ts index 2f92766fc67..182dd066340 100644 --- a/apps/sim/tools/confluence/delete_label.ts +++ b/apps/sim/tools/confluence/delete_label.ts @@ -33,6 +33,8 @@ export const confluenceDeleteLabelTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_page.ts b/apps/sim/tools/confluence/delete_page.ts index a648a2b37c5..bc7be2b8932 100644 --- a/apps/sim/tools/confluence/delete_page.ts +++ b/apps/sim/tools/confluence/delete_page.ts @@ -32,6 +32,8 @@ export const confluenceDeletePageTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_page_property.ts b/apps/sim/tools/confluence/delete_page_property.ts index d7b6c5fbb49..0bd4d04911b 100644 --- a/apps/sim/tools/confluence/delete_page_property.ts +++ b/apps/sim/tools/confluence/delete_page_property.ts @@ -33,6 +33,8 @@ export const confluenceDeletePagePropertyTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_space.ts b/apps/sim/tools/confluence/delete_space.ts index 82442c66ad5..c9fa5cb315e 100644 --- a/apps/sim/tools/confluence/delete_space.ts +++ b/apps/sim/tools/confluence/delete_space.ts @@ -31,6 +31,8 @@ export const confluenceDeleteSpaceTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/delete_space_property.ts b/apps/sim/tools/confluence/delete_space_property.ts index 9c69431aac4..0f2a29dfdd4 100644 --- a/apps/sim/tools/confluence/delete_space_property.ts +++ b/apps/sim/tools/confluence/delete_space_property.ts @@ -33,6 +33,8 @@ export const confluenceDeleteSpacePropertyTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_blogpost.ts b/apps/sim/tools/confluence/get_blogpost.ts index 94c9b02de7c..c61d4894117 100644 --- a/apps/sim/tools/confluence/get_blogpost.ts +++ b/apps/sim/tools/confluence/get_blogpost.ts @@ -49,6 +49,8 @@ export const confluenceGetBlogPostTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_page_ancestors.ts b/apps/sim/tools/confluence/get_page_ancestors.ts index 20b7be3ca2c..2e3ea9714e2 100644 --- a/apps/sim/tools/confluence/get_page_ancestors.ts +++ b/apps/sim/tools/confluence/get_page_ancestors.ts @@ -39,6 +39,8 @@ export const confluenceGetPageAncestorsTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_page_children.ts b/apps/sim/tools/confluence/get_page_children.ts index 7ca7ca10eda..a7691245ea6 100644 --- a/apps/sim/tools/confluence/get_page_children.ts +++ b/apps/sim/tools/confluence/get_page_children.ts @@ -42,6 +42,8 @@ export const confluenceGetPageChildrenTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_page_descendants.ts b/apps/sim/tools/confluence/get_page_descendants.ts index a9e0bc5a323..9fdb8213331 100644 --- a/apps/sim/tools/confluence/get_page_descendants.ts +++ b/apps/sim/tools/confluence/get_page_descendants.ts @@ -43,6 +43,8 @@ export const confluenceGetPageDescendantsTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_page_version.ts b/apps/sim/tools/confluence/get_page_version.ts index dc496b38a24..a67b709295c 100644 --- a/apps/sim/tools/confluence/get_page_version.ts +++ b/apps/sim/tools/confluence/get_page_version.ts @@ -54,6 +54,8 @@ export const confluenceGetPageVersionTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_pages_by_label.ts b/apps/sim/tools/confluence/get_pages_by_label.ts index af67210a0b0..0220341ed16 100644 --- a/apps/sim/tools/confluence/get_pages_by_label.ts +++ b/apps/sim/tools/confluence/get_pages_by_label.ts @@ -47,6 +47,8 @@ export const confluenceGetPagesByLabelTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_space.ts b/apps/sim/tools/confluence/get_space.ts index fbadd7a6575..178ec6e21f1 100644 --- a/apps/sim/tools/confluence/get_space.ts +++ b/apps/sim/tools/confluence/get_space.ts @@ -42,6 +42,8 @@ export const confluenceGetSpaceTool: ToolConfig< provider: 'confluence', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/confluence/get_task.ts b/apps/sim/tools/confluence/get_task.ts index cf0b6177654..21c2c5c4284 100644 --- a/apps/sim/tools/confluence/get_task.ts +++ b/apps/sim/tools/confluence/get_task.ts @@ -41,6 +41,8 @@ export const confluenceGetTaskTool: ToolConfig errorInfo?.data?.errorMessage, + description: + 'Atlassian REST API error formats (errorMessage, errorMessages, errors[].title, message)', + examples: ['Jira', 'Jira Service Management', 'Confluence', 'JSM Forms/ProForma'], + extract: (errorInfo) => { + // JSM Service Desk: singular errorMessage string + if (errorInfo?.data?.errorMessage) { + return errorInfo.data.errorMessage + } + // Jira Platform: errorMessages array + if ( + Array.isArray(errorInfo?.data?.errorMessages) && + errorInfo.data.errorMessages.length > 0 + ) { + return errorInfo.data.errorMessages.join(', ') + } + // Confluence v2 / Forms API: RFC 7807 errors array with title/detail + if (Array.isArray(errorInfo?.data?.errors) && errorInfo.data.errors.length > 0) { + const err = errorInfo.data.errors[0] + if (err?.title) { + return err.detail ? `${err.title}: ${err.detail}` : err.title + } + } + // Jira Platform field-level errors object + if (errorInfo?.data?.errors && !Array.isArray(errorInfo.data.errors)) { + const fieldErrors = Object.entries(errorInfo.data.errors) + .map(([field, msg]) => `${field}: ${msg}`) + .join(', ') + if (fieldErrors) return fieldErrors + } + // Generic message fallback (auth/gateway errors) + if (errorInfo?.data?.message) { + return errorInfo.data.message + } + return undefined + }, }, { id: 'graphql-errors', diff --git a/apps/sim/tools/index.test.ts b/apps/sim/tools/index.test.ts index e3ecf97fae2..cef7cc82a19 100644 --- a/apps/sim/tools/index.test.ts +++ b/apps/sim/tools/index.test.ts @@ -26,6 +26,8 @@ const { mockListCustomTools, mockGetCustomToolByIdOrTitle, mockGenerateInternalToken, + mockSecureFetchWithPinnedIP, + mockValidateUrlWithDNS, } = vi.hoisted(() => ({ mockIsHosted: { value: false }, mockEnv: { NEXT_PUBLIC_APP_URL: 'http://localhost:3000' } as Record, @@ -40,6 +42,8 @@ const { mockListCustomTools: vi.fn(), mockGetCustomToolByIdOrTitle: vi.fn(), mockGenerateInternalToken: vi.fn(), + mockSecureFetchWithPinnedIP: vi.fn(), + mockValidateUrlWithDNS: vi.fn(), })) // Mock feature flags @@ -73,6 +77,11 @@ vi.mock('@/lib/auth/internal', () => ({ vi.mock('@/lib/billing/core/usage-log', () => ({})) +vi.mock('@/lib/core/security/input-validation.server', () => ({ + secureFetchWithPinnedIP: (...args: unknown[]) => mockSecureFetchWithPinnedIP(...args), + validateUrlWithDNS: (...args: unknown[]) => mockValidateUrlWithDNS(...args), +})) + vi.mock('@/lib/core/rate-limiter/hosted-key', () => ({ getHostedKeyRateLimiter: () => mockRateLimiterFns, })) @@ -476,6 +485,19 @@ describe('Automatic Internal Route Detection', () => { beforeEach(() => { process.env.NEXT_PUBLIC_APP_URL = 'http://localhost:3000' cleanupEnvVars = setupEnvVars({ NEXT_PUBLIC_APP_URL: 'http://localhost:3000' }) + + mockValidateUrlWithDNS.mockResolvedValue({ isValid: true, resolvedIP: '93.184.216.34' }) + mockSecureFetchWithPinnedIP.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + headers: { + get: (name: string) => (name.toLowerCase() === 'content-type' ? 'application/json' : null), + toRecord: () => ({ 'content-type': 'application/json' }), + }, + text: async () => JSON.stringify({}), + json: async () => ({}), + }) }) afterEach(() => { diff --git a/apps/sim/tools/jira/add_attachment.ts b/apps/sim/tools/jira/add_attachment.ts index bd890e509ed..b9eeba374c1 100644 --- a/apps/sim/tools/jira/add_attachment.ts +++ b/apps/sim/tools/jira/add_attachment.ts @@ -14,6 +14,8 @@ export const jiraAddAttachmentTool: ToolConfig = provider: 'jira', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/jira/update_comment.ts b/apps/sim/tools/jira/update_comment.ts index 526e724fb4c..e49fa248332 100644 --- a/apps/sim/tools/jira/update_comment.ts +++ b/apps/sim/tools/jira/update_comment.ts @@ -31,6 +31,8 @@ export const jiraUpdateCommentTool: ToolConfig = { provider: 'jira', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/jsm/add_comment.ts b/apps/sim/tools/jsm/add_comment.ts index e23069a138a..a06caaf36ca 100644 --- a/apps/sim/tools/jsm/add_comment.ts +++ b/apps/sim/tools/jsm/add_comment.ts @@ -13,6 +13,8 @@ export const jsmAddCommentTool: ToolConfig = { provider: 'jira', }, + errorExtractor: 'atlassian-errors', + params: { accessToken: { type: 'string', diff --git a/apps/sim/tools/jsm/get_transitions.ts b/apps/sim/tools/jsm/get_transitions.ts index d864cf747d6..d439c174cea 100644 --- a/apps/sim/tools/jsm/get_transitions.ts +++ b/apps/sim/tools/jsm/get_transitions.ts @@ -14,6 +14,8 @@ export const jsmGetTransitionsTool: ToolConfig