Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
19 changes: 19 additions & 0 deletions apps/sim/lib/api/contracts/credential-groups.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
credentialGroupAccessResponseSchema,
credentialGroupEnrollmentDetailSchema,
credentialGroupEnrollmentListQuerySchema,
credentialGroupOAuthCallbackQuerySchema,
credentialGroupSchema,
inviteCredentialGroupEnrollmentsBodySchema,
sharedCredentialGroupOAuthCallbackContract,
Expand Down Expand Up @@ -292,4 +293,22 @@ describe('credential group contracts', () => {
}).success
).toBe(false)
})

it('accepts an Atlassian-sized authorization code', () => {
const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({
state: `cg_${'a'.repeat(36)}`,
code: 'a'.repeat(4096),
})

expect(parsed.success).toBe(true)
})

it('still rejects an unbounded authorization code', () => {
const parsed = credentialGroupOAuthCallbackQuerySchema.safeParse({
state: `cg_${'a'.repeat(36)}`,
code: 'a'.repeat(8193),
})

expect(parsed.success).toBe(false)
})
})
8 changes: 6 additions & 2 deletions apps/sim/lib/api/contracts/credential-groups.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { z } from 'zod'
import { workflowIdSchema, workspaceIdSchema } from '@/lib/api/contracts/primitives'
import {
MAX_OAUTH_CODE_LENGTH,
workflowIdSchema,
workspaceIdSchema,
} from '@/lib/api/contracts/primitives'
import { defineRouteContract } from '@/lib/api/contracts/types'
import {
CREDENTIAL_GROUP_MCP_SERVER_LIMIT,
Expand Down Expand Up @@ -234,7 +238,7 @@ export const startCredentialGroupMcpOAuthParamsSchema =
export const credentialGroupOAuthCallbackQuerySchema = z
.object({
state: z.string().min(1, 'OAuth state is required').max(512),
code: z.string().min(1).max(2048).optional(),
code: z.string().min(1).max(MAX_OAUTH_CODE_LENGTH, 'Authorization code is too long').optional(),
error: z.string().min(1).max(256).optional(),
error_description: z.string().max(1000).optional(),
})
Expand Down
3 changes: 1 addition & 2 deletions apps/sim/lib/api/contracts/oauth-connections.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod'
import { workspaceIdSchema } from '@/lib/api/contracts/primitives'
import { MAX_OAUTH_CODE_LENGTH, workspaceIdSchema } from '@/lib/api/contracts/primitives'
import type {
ContractBody,
ContractBodyInput,
Expand Down Expand Up @@ -223,7 +223,6 @@ export const trelloCallbackContract = defineRouteContract({
})

const MAX_OAUTH_RETURN_URL_LENGTH = 2048
const MAX_OAUTH_CODE_LENGTH = 8192
const MAX_OAUTH_STATE_LENGTH = 256
const MAX_OAUTH_ERROR_LENGTH = 2048

Expand Down
11 changes: 11 additions & 0 deletions apps/sim/lib/api/contracts/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,17 @@ export function withMissingFieldMessage<TSchema extends z.ZodString>(
*/
export const MAX_ID_LENGTH = 128

/**
* Bound for an OAuth `code` callback parameter.
*
* Authorization codes have no length ceiling in RFC 6749, and providers differ by
* orders of magnitude: Slack's are tens of characters while Atlassian returns a
* signed JWT that routinely exceeds 2KB. The bound exists to keep an unbounded
* string out of a token exchange, so it is sized above the largest real code
* rather than around any one provider.
*/
export const MAX_OAUTH_CODE_LENGTH = 8192

/**
* Builds a required, non-empty string schema whose message covers **both**
* failure modes.
Expand Down
Loading