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
39 changes: 33 additions & 6 deletions apps/sim/app/api/tools/jsm/approvals/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import {
validateAlphanumericId,
validateEnum,
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'

const logger = createLogger('JsmApprovalsAPI')

const VALID_ACTIONS = ['get', 'answer'] as const
const VALID_DECISIONS = ['approve', 'decline'] as const

export async function POST(request: Request) {
try {
const body = await request.json()
Expand Down Expand Up @@ -41,7 +50,23 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Action is required' }, { status: 400 })
}

const actionValidation = validateEnum(action, VALID_ACTIONS, 'action')
if (!actionValidation.isValid) {
return NextResponse.json({ error: actionValidation.error }, { status: 400 })
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const issueIdOrKeyValidation = validateJiraIssueKey(issueIdOrKey, 'issueIdOrKey')
if (!issueIdOrKeyValidation.isValid) {
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

if (action === 'get') {
Expand Down Expand Up @@ -91,12 +116,14 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Approval ID is required' }, { status: 400 })
}

if (!decision || !['approve', 'decline'].includes(decision)) {
logger.error('Invalid or missing decision in request')
return NextResponse.json(
{ error: 'Decision is required and must be "approve" or "decline"' },
{ status: 400 }
)
const approvalIdValidation = validateAlphanumericId(approvalId, 'approvalId')
if (!approvalIdValidation.isValid) {
return NextResponse.json({ error: approvalIdValidation.error }, { status: 400 })
}

const decisionValidation = validateEnum(decision, VALID_DECISIONS, 'decision')
if (!decisionValidation.isValid) {
return NextResponse.json({ error: decisionValidation.error }, { status: 400 })
}

const url = `${baseUrl}/request/${issueIdOrKey}/approval/${approvalId}`
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/comment/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -38,6 +39,17 @@ export async function POST(request: Request) {
}

const cloudId = providedCloudId || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const issueIdOrKeyValidation = validateJiraIssueKey(issueIdOrKey, 'issueIdOrKey')
if (!issueIdOrKeyValidation.isValid) {
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const url = `${baseUrl}/request/${issueIdOrKey}/comment`
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/comments/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -36,6 +37,17 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const issueIdOrKeyValidation = validateJiraIssueKey(issueIdOrKey, 'issueIdOrKey')
if (!issueIdOrKeyValidation.isValid) {
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const params = new URLSearchParams()
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/customers/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -36,6 +37,17 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const parsedEmails = emails
Expand Down
28 changes: 28 additions & 0 deletions apps/sim/app/api/tools/jsm/organization/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import {
validateAlphanumericId,
validateEnum,
validateJiraCloudId,
} from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'

const logger = createLogger('JsmOrganizationAPI')

const VALID_ACTIONS = ['create', 'add_to_service_desk'] as const

export async function POST(request: Request) {
try {
const body = await request.json()
Expand Down Expand Up @@ -34,7 +41,18 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Action is required' }, { status: 400 })
}

const actionValidation = validateEnum(action, VALID_ACTIONS, 'action')
if (!actionValidation.isValid) {
return NextResponse.json({ error: actionValidation.error }, { status: 400 })
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

if (action === 'create') {
Expand Down Expand Up @@ -90,6 +108,16 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Organization ID is required' }, { status: 400 })
}

const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const organizationIdValidation = validateAlphanumericId(organizationId, 'organizationId')
if (!organizationIdValidation.isValid) {
return NextResponse.json({ error: organizationIdValidation.error }, { status: 400 })
}

const url = `${baseUrl}/servicedesk/${serviceDeskId}/organization`

logger.info('Adding organization to service desk:', { serviceDeskId, organizationId })
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/organizations/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -27,6 +28,17 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const params = new URLSearchParams()
Expand Down
23 changes: 23 additions & 0 deletions apps/sim/app/api/tools/jsm/participants/route.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import {
validateEnum,
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'

const logger = createLogger('JsmParticipantsAPI')

const VALID_ACTIONS = ['get', 'add'] as const

export async function POST(request: Request) {
try {
const body = await request.json()
Expand Down Expand Up @@ -40,7 +47,23 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Action is required' }, { status: 400 })
}

const actionValidation = validateEnum(action, VALID_ACTIONS, 'action')
if (!actionValidation.isValid) {
return NextResponse.json({ error: actionValidation.error }, { status: 400 })
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const issueIdOrKeyValidation = validateJiraIssueKey(issueIdOrKey, 'issueIdOrKey')
if (!issueIdOrKeyValidation.isValid) {
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

if (action === 'get') {
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/queues/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -35,6 +36,17 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const params = new URLSearchParams()
Expand Down
25 changes: 25 additions & 0 deletions apps/sim/app/api/tools/jsm/request/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import {
validateAlphanumericId,
validateJiraCloudId,
validateJiraIssueKey,
} from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -33,11 +38,26 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const isCreateOperation = serviceDeskId && requestTypeId && summary

if (isCreateOperation) {
const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const requestTypeIdValidation = validateAlphanumericId(requestTypeId, 'requestTypeId')
if (!requestTypeIdValidation.isValid) {
return NextResponse.json({ error: requestTypeIdValidation.error }, { status: 400 })
}
const url = `${baseUrl}/request`

logger.info('Creating request at:', url)
Expand Down Expand Up @@ -95,6 +115,11 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Issue ID or key is required' }, { status: 400 })
}

const issueIdOrKeyValidation = validateJiraIssueKey(issueIdOrKey, 'issueIdOrKey')
if (!issueIdOrKeyValidation.isValid) {
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
}

const url = `${baseUrl}/request/${issueIdOrKey}`

logger.info('Fetching request from:', url)
Expand Down
14 changes: 14 additions & 0 deletions apps/sim/app/api/tools/jsm/requests/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -32,6 +33,19 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

if (serviceDeskId) {
const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const params = new URLSearchParams()
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/api/tools/jsm/requesttypes/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createLogger } from '@sim/logger'
import { NextResponse } from 'next/server'
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'

export const dynamic = 'force-dynamic'
Expand Down Expand Up @@ -27,6 +28,17 @@ export async function POST(request: Request) {
}

const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))

const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
if (!cloudIdValidation.isValid) {
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
}

const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
if (!serviceDeskIdValidation.isValid) {
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
}

const baseUrl = getJsmApiBaseUrl(cloudId)

const params = new URLSearchParams()
Expand Down
Loading