mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
fix(vulns): fix various vulnerabilities and enhanced code security (#1611)
* fix(vulns): fix SSRF vulnerabilities * cleanup * cleanup * regen docs * remove unused deps * fix failing tests * cleanup * update deps * regen bun lock
This commit is contained in:
@@ -168,7 +168,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
// Mock checkpoint found but workflow not found
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'a1b2c3d4-e5f6-4a78-b9c0-d1e2f3a4b5c6',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
@@ -196,13 +196,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
// Mock checkpoint found but workflow belongs to different user
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'b2c3d4e5-f6a7-4b89-a0d1-e2f3a4b5c6d7',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'b2c3d4e5-f6a7-4b89-a0d1-e2f3a4b5c6d7',
|
||||
userId: 'different-user',
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'c3d4e5f6-a7b8-4c09-a1e2-f3a4b5c6d7e8',
|
||||
userId: 'user-123',
|
||||
workflowState: {
|
||||
blocks: { block1: { type: 'start' } },
|
||||
@@ -241,7 +241,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'c3d4e5f6-a7b8-4c09-a1e2-f3a4b5c6d7e8',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
const responseData = await response.json()
|
||||
expect(responseData).toEqual({
|
||||
success: true,
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'c3d4e5f6-a7b8-4c09-a1e2-f3a4b5c6d7e8',
|
||||
checkpointId: 'checkpoint-123',
|
||||
revertedAt: '2024-01-01T00:00:00.000Z',
|
||||
checkpoint: {
|
||||
@@ -293,7 +293,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
// Verify fetch was called with correct parameters
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'http://localhost:3000/api/workflows/workflow-456/state',
|
||||
'http://localhost:3000/api/workflows/c3d4e5f6-a7b8-4c09-a1e2-f3a4b5c6d7e8/state',
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -319,7 +319,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-with-date',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'd4e5f6a7-b8c9-4d10-a2e3-a4b5c6d7e8f9',
|
||||
userId: 'user-123',
|
||||
workflowState: {
|
||||
blocks: {},
|
||||
@@ -330,7 +330,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'd4e5f6a7-b8c9-4d10-a2e3-a4b5c6d7e8f9',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-invalid-date',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'e5f6a7b8-c9d0-4e11-a3f4-b5c6d7e8f9a0',
|
||||
userId: 'user-123',
|
||||
workflowState: {
|
||||
blocks: {},
|
||||
@@ -371,7 +371,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'e5f6a7b8-c9d0-4e11-a3f4-b5c6d7e8f9a0',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-null-values',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'f6a7b8c9-d0e1-4f23-a4b5-c6d7e8f9a0b1',
|
||||
userId: 'user-123',
|
||||
workflowState: {
|
||||
blocks: null,
|
||||
@@ -413,7 +413,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'f6a7b8c9-d0e1-4f23-a4b5-c6d7e8f9a0b1',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -452,13 +452,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'a7b8c9d0-e1f2-4a34-b5c6-d7e8f9a0b1c2',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'a7b8c9d0-e1f2-4a34-b5c6-d7e8f9a0b1c2',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'b8c9d0e1-f2a3-4b45-a6d7-e8f9a0b1c2d3',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
@@ -537,13 +537,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'c9d0e1f2-a3b4-4c56-a7e8-f9a0b1c2d3e4',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'c9d0e1f2-a3b4-4c56-a7e8-f9a0b1c2d3e4',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -594,13 +594,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'd0e1f2a3-b4c5-4d67-a8f9-a0b1c2d3e4f5',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'd0e1f2a3-b4c5-4d67-a8f9-a0b1c2d3e4f5',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
await POST(req)
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'http://localhost:3000/api/workflows/workflow-456/state',
|
||||
'http://localhost:3000/api/workflows/d0e1f2a3-b4c5-4d67-a8f9-a0b1c2d3e4f5/state',
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -644,13 +644,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-123',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'e1f2a3b4-c5d6-4e78-a9a0-b1c2d3e4f5a6',
|
||||
userId: 'user-123',
|
||||
workflowState: { blocks: {}, edges: [] },
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'e1f2a3b4-c5d6-4e78-a9a0-b1c2d3e4f5a6',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
@@ -677,7 +677,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
'http://localhost:3000/api/workflows/workflow-456/state',
|
||||
'http://localhost:3000/api/workflows/e1f2a3b4-c5d6-4e78-a9a0-b1c2d3e4f5a6/state',
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -695,7 +695,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
|
||||
const mockCheckpoint = {
|
||||
id: 'checkpoint-complex',
|
||||
workflowId: 'workflow-456',
|
||||
workflowId: 'f2a3b4c5-d6e7-4f89-a0b1-c2d3e4f5a6b7',
|
||||
userId: 'user-123',
|
||||
workflowState: {
|
||||
blocks: {
|
||||
@@ -723,7 +723,7 @@ describe('Copilot Checkpoints Revert API Route', () => {
|
||||
}
|
||||
|
||||
const mockWorkflow = {
|
||||
id: 'workflow-456',
|
||||
id: 'f2a3b4c5-d6e7-4f89-a0b1-c2d3e4f5a6b7',
|
||||
userId: 'user-123',
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
createUnauthorizedResponse,
|
||||
} from '@/lib/copilot/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateUUID } from '@/lib/security/input-validation'
|
||||
|
||||
const logger = createLogger('CheckpointRevertAPI')
|
||||
|
||||
@@ -36,7 +37,6 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
logger.info(`[${tracker.requestId}] Reverting to checkpoint ${checkpointId}`)
|
||||
|
||||
// Get the checkpoint and verify ownership
|
||||
const checkpoint = await db
|
||||
.select()
|
||||
.from(workflowCheckpoints)
|
||||
@@ -47,7 +47,6 @@ export async function POST(request: NextRequest) {
|
||||
return createNotFoundResponse('Checkpoint not found or access denied')
|
||||
}
|
||||
|
||||
// Verify user still has access to the workflow
|
||||
const workflowData = await db
|
||||
.select()
|
||||
.from(workflowTable)
|
||||
@@ -62,10 +61,8 @@ export async function POST(request: NextRequest) {
|
||||
return createUnauthorizedResponse()
|
||||
}
|
||||
|
||||
// Apply the checkpoint state to the workflow using the existing state endpoint
|
||||
const checkpointState = checkpoint.workflowState as any // Cast to any for property access
|
||||
|
||||
// Clean the checkpoint state to remove any null/undefined values that could cause validation errors
|
||||
const cleanedState = {
|
||||
blocks: checkpointState?.blocks || {},
|
||||
edges: checkpointState?.edges || [],
|
||||
@@ -74,7 +71,6 @@ export async function POST(request: NextRequest) {
|
||||
isDeployed: checkpointState?.isDeployed || false,
|
||||
deploymentStatuses: checkpointState?.deploymentStatuses || {},
|
||||
lastSaved: Date.now(),
|
||||
// Only include deployedAt if it's a valid date string that can be converted
|
||||
...(checkpointState?.deployedAt &&
|
||||
checkpointState.deployedAt !== null &&
|
||||
checkpointState.deployedAt !== undefined &&
|
||||
@@ -90,13 +86,19 @@ export async function POST(request: NextRequest) {
|
||||
isDeployed: cleanedState.isDeployed,
|
||||
})
|
||||
|
||||
const workflowIdValidation = validateUUID(checkpoint.workflowId, 'workflowId')
|
||||
if (!workflowIdValidation.isValid) {
|
||||
logger.error(`[${tracker.requestId}] Invalid workflow ID: ${workflowIdValidation.error}`)
|
||||
return NextResponse.json({ error: 'Invalid workflow ID format' }, { status: 400 })
|
||||
}
|
||||
|
||||
const stateResponse = await fetch(
|
||||
`${request.nextUrl.origin}/api/workflows/${checkpoint.workflowId}/state`,
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Cookie: request.headers.get('Cookie') || '', // Forward auth cookies
|
||||
Cookie: request.headers.get('Cookie') || '',
|
||||
},
|
||||
body: JSON.stringify(cleanedState),
|
||||
}
|
||||
@@ -123,7 +125,7 @@ export async function POST(request: NextRequest) {
|
||||
revertedAt: new Date().toISOString(),
|
||||
checkpoint: {
|
||||
id: checkpoint.id,
|
||||
workflowState: cleanedState, // Return the reverted state for frontend use
|
||||
workflowState: cleanedState,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user