diff --git a/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts index 60e3113ce0..38c689cf68 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-alarms/route.ts @@ -9,12 +9,18 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' const logger = createLogger('CloudWatchDescribeAlarms') const DescribeAlarmsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), alarmNamePrefix: z.string().optional(), diff --git a/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts index 9051d0ac64..b58c9cfe8a 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-log-groups/route.ts @@ -4,13 +4,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createCloudWatchLogsClient } from '@/app/api/tools/cloudwatch/utils' const logger = createLogger('CloudWatchDescribeLogGroups') const DescribeLogGroupsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), prefix: z.string().optional(), diff --git a/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts b/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts index 09db38dcc4..5a79264236 100644 --- a/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/describe-log-streams/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createCloudWatchLogsClient, describeLogStreams } from '@/app/api/tools/cloudwatch/utils' const logger = createLogger('CloudWatchDescribeLogStreams') const DescribeLogStreamsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), logGroupName: z.string().min(1, 'Log group name is required'), diff --git a/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts b/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts index 4ccd1bc792..60c1324649 100644 --- a/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/get-log-events/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createCloudWatchLogsClient, getLogEvents } from '@/app/api/tools/cloudwatch/utils' const logger = createLogger('CloudWatchGetLogEvents') const GetLogEventsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), logGroupName: z.string().min(1, 'Log group name is required'), diff --git a/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts b/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts index acd8756f10..f19fe2aeba 100644 --- a/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/get-metric-statistics/route.ts @@ -4,12 +4,18 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' const logger = createLogger('CloudWatchGetMetricStatistics') const GetMetricStatisticsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), namespace: z.string().min(1, 'Namespace is required'), diff --git a/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts b/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts index 711d226543..9d9443cb3b 100644 --- a/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/list-metrics/route.ts @@ -4,12 +4,18 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' const logger = createLogger('CloudWatchListMetrics') const ListMetricsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), namespace: z.string().optional(), diff --git a/apps/sim/app/api/tools/cloudwatch/put-metric-data/route.ts b/apps/sim/app/api/tools/cloudwatch/put-metric-data/route.ts index 86886d7a92..dc69f04d49 100644 --- a/apps/sim/app/api/tools/cloudwatch/put-metric-data/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/put-metric-data/route.ts @@ -8,6 +8,7 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' const logger = createLogger('CloudWatchPutMetricData') @@ -43,7 +44,12 @@ const VALID_UNITS = [ ] as const const PutMetricDataSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), namespace: z.string().min(1, 'Namespace is required'), diff --git a/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts b/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts index 822eb14a5d..473f89c655 100644 --- a/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts +++ b/apps/sim/app/api/tools/cloudwatch/query-logs/route.ts @@ -4,13 +4,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createCloudWatchLogsClient, pollQueryResults } from '@/app/api/tools/cloudwatch/utils' const logger = createLogger('CloudWatchQueryLogs') const QueryLogsSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), logGroupNames: z.array(z.string().min(1)).min(1, 'At least one log group name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/delete/route.ts b/apps/sim/app/api/tools/dynamodb/delete/route.ts index 0d883c7c10..f51c9704c5 100644 --- a/apps/sim/app/api/tools/dynamodb/delete/route.ts +++ b/apps/sim/app/api/tools/dynamodb/delete/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, deleteItem } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBDeleteAPI') const DeleteSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/get/route.ts b/apps/sim/app/api/tools/dynamodb/get/route.ts index 752f7360b0..1356105eab 100644 --- a/apps/sim/app/api/tools/dynamodb/get/route.ts +++ b/apps/sim/app/api/tools/dynamodb/get/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, getItem } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBGetAPI') const GetSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/introspect/route.ts b/apps/sim/app/api/tools/dynamodb/introspect/route.ts index 33db3e79ff..ee8ea19360 100644 --- a/apps/sim/app/api/tools/dynamodb/introspect/route.ts +++ b/apps/sim/app/api/tools/dynamodb/introspect/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createRawDynamoDBClient, describeTable, listTables } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBIntrospectAPI') const IntrospectSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().optional(), diff --git a/apps/sim/app/api/tools/dynamodb/put/route.ts b/apps/sim/app/api/tools/dynamodb/put/route.ts index 9adb667ab9..f88ab229c8 100644 --- a/apps/sim/app/api/tools/dynamodb/put/route.ts +++ b/apps/sim/app/api/tools/dynamodb/put/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, putItem } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBPutAPI') const PutSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/query/route.ts b/apps/sim/app/api/tools/dynamodb/query/route.ts index 3b0d137c1f..4f5acd119a 100644 --- a/apps/sim/app/api/tools/dynamodb/query/route.ts +++ b/apps/sim/app/api/tools/dynamodb/query/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, queryItems } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBQueryAPI') const QuerySchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/scan/route.ts b/apps/sim/app/api/tools/dynamodb/scan/route.ts index 8e9d401c03..1e1630e248 100644 --- a/apps/sim/app/api/tools/dynamodb/scan/route.ts +++ b/apps/sim/app/api/tools/dynamodb/scan/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, scanItems } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBScanAPI') const ScanSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/dynamodb/update/route.ts b/apps/sim/app/api/tools/dynamodb/update/route.ts index f7884ad502..0342688bac 100644 --- a/apps/sim/app/api/tools/dynamodb/update/route.ts +++ b/apps/sim/app/api/tools/dynamodb/update/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createDynamoDBClient, updateItem } from '@/app/api/tools/dynamodb/utils' const logger = createLogger('DynamoDBUpdateAPI') const UpdateSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), tableName: z.string().min(1, 'Table name is required'), diff --git a/apps/sim/app/api/tools/iam/add-user-to-group/route.ts b/apps/sim/app/api/tools/iam/add-user-to-group/route.ts index 71d500559c..34dd4fbe6a 100644 --- a/apps/sim/app/api/tools/iam/add-user-to-group/route.ts +++ b/apps/sim/app/api/tools/iam/add-user-to-group/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { addUserToGroup, createIAMClient } from '../utils' const logger = createLogger('IAMAddUserToGroupAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/attach-role-policy/route.ts b/apps/sim/app/api/tools/iam/attach-role-policy/route.ts index 2b0a7a7c06..570b17ea85 100644 --- a/apps/sim/app/api/tools/iam/attach-role-policy/route.ts +++ b/apps/sim/app/api/tools/iam/attach-role-policy/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { attachRolePolicy, createIAMClient } from '../utils' const logger = createLogger('IAMAttachRolePolicyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/attach-user-policy/route.ts b/apps/sim/app/api/tools/iam/attach-user-policy/route.ts index 992caf2ab5..de722bb5cb 100644 --- a/apps/sim/app/api/tools/iam/attach-user-policy/route.ts +++ b/apps/sim/app/api/tools/iam/attach-user-policy/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { attachUserPolicy, createIAMClient } from '../utils' const logger = createLogger('IAMAttachUserPolicyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/create-access-key/route.ts b/apps/sim/app/api/tools/iam/create-access-key/route.ts index 03fcb73139..1acd770787 100644 --- a/apps/sim/app/api/tools/iam/create-access-key/route.ts +++ b/apps/sim/app/api/tools/iam/create-access-key/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createAccessKey, createIAMClient } from '../utils' const logger = createLogger('IAMCreateAccessKeyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/create-role/route.ts b/apps/sim/app/api/tools/iam/create-role/route.ts index 67b0b49fdb..c065ecbfd1 100644 --- a/apps/sim/app/api/tools/iam/create-role/route.ts +++ b/apps/sim/app/api/tools/iam/create-role/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, createRole } from '../utils' const logger = createLogger('IAMCreateRoleAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/create-user/route.ts b/apps/sim/app/api/tools/iam/create-user/route.ts index 7b77a9c4d2..288f97140c 100644 --- a/apps/sim/app/api/tools/iam/create-user/route.ts +++ b/apps/sim/app/api/tools/iam/create-user/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, createUser } from '../utils' const logger = createLogger('IAMCreateUserAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/delete-access-key/route.ts b/apps/sim/app/api/tools/iam/delete-access-key/route.ts index 9fadebf8b0..7023abafb9 100644 --- a/apps/sim/app/api/tools/iam/delete-access-key/route.ts +++ b/apps/sim/app/api/tools/iam/delete-access-key/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, deleteAccessKey } from '../utils' const logger = createLogger('IAMDeleteAccessKeyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), accessKeyIdToDelete: z.string().min(1, 'Access key ID to delete is required'), diff --git a/apps/sim/app/api/tools/iam/delete-role/route.ts b/apps/sim/app/api/tools/iam/delete-role/route.ts index cc2efacff1..0e399ac03e 100644 --- a/apps/sim/app/api/tools/iam/delete-role/route.ts +++ b/apps/sim/app/api/tools/iam/delete-role/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, deleteRole } from '../utils' const logger = createLogger('IAMDeleteRoleAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/delete-user/route.ts b/apps/sim/app/api/tools/iam/delete-user/route.ts index 9e190fa65a..ec9a30b1d7 100644 --- a/apps/sim/app/api/tools/iam/delete-user/route.ts +++ b/apps/sim/app/api/tools/iam/delete-user/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, deleteUser } from '../utils' const logger = createLogger('IAMDeleteUserAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/detach-role-policy/route.ts b/apps/sim/app/api/tools/iam/detach-role-policy/route.ts index bbc4a96f5d..02e4846566 100644 --- a/apps/sim/app/api/tools/iam/detach-role-policy/route.ts +++ b/apps/sim/app/api/tools/iam/detach-role-policy/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, detachRolePolicy } from '../utils' const logger = createLogger('IAMDetachRolePolicyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/detach-user-policy/route.ts b/apps/sim/app/api/tools/iam/detach-user-policy/route.ts index 47a2d547ba..12fb38f1ba 100644 --- a/apps/sim/app/api/tools/iam/detach-user-policy/route.ts +++ b/apps/sim/app/api/tools/iam/detach-user-policy/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, detachUserPolicy } from '../utils' const logger = createLogger('IAMDetachUserPolicyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/get-role/route.ts b/apps/sim/app/api/tools/iam/get-role/route.ts index eec511d7fb..2efdbfa636 100644 --- a/apps/sim/app/api/tools/iam/get-role/route.ts +++ b/apps/sim/app/api/tools/iam/get-role/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, getRole } from '../utils' const logger = createLogger('IAMGetRoleAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/get-user/route.ts b/apps/sim/app/api/tools/iam/get-user/route.ts index 3f19dd2573..83f9a2dd5e 100644 --- a/apps/sim/app/api/tools/iam/get-user/route.ts +++ b/apps/sim/app/api/tools/iam/get-user/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, getUser } from '../utils' const logger = createLogger('IAMGetUserAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1).optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/list-attached-role-policies/route.ts b/apps/sim/app/api/tools/iam/list-attached-role-policies/route.ts index ddfb1c0b08..1ff209e96e 100644 --- a/apps/sim/app/api/tools/iam/list-attached-role-policies/route.ts +++ b/apps/sim/app/api/tools/iam/list-attached-role-policies/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listAttachedRolePolicies } from '../utils' const logger = createLogger('IAMListAttachedRolePoliciesAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleName: z.string().min(1, 'Role name is required'), diff --git a/apps/sim/app/api/tools/iam/list-attached-user-policies/route.ts b/apps/sim/app/api/tools/iam/list-attached-user-policies/route.ts index c648ccb607..f46ed5cfd0 100644 --- a/apps/sim/app/api/tools/iam/list-attached-user-policies/route.ts +++ b/apps/sim/app/api/tools/iam/list-attached-user-policies/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listAttachedUserPolicies } from '../utils' const logger = createLogger('IAMListAttachedUserPoliciesAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/list-groups/route.ts b/apps/sim/app/api/tools/iam/list-groups/route.ts index 11c2444ae4..3fe2aba916 100644 --- a/apps/sim/app/api/tools/iam/list-groups/route.ts +++ b/apps/sim/app/api/tools/iam/list-groups/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listGroups } from '../utils' const logger = createLogger('IAMListGroupsAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), pathPrefix: z.string().optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/list-policies/route.ts b/apps/sim/app/api/tools/iam/list-policies/route.ts index ef2a5aaa01..ad1d232144 100644 --- a/apps/sim/app/api/tools/iam/list-policies/route.ts +++ b/apps/sim/app/api/tools/iam/list-policies/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listPolicies } from '../utils' const logger = createLogger('IAMListPoliciesAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), scope: z.string().optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/list-roles/route.ts b/apps/sim/app/api/tools/iam/list-roles/route.ts index ffc7168b0b..b6e7eafdc6 100644 --- a/apps/sim/app/api/tools/iam/list-roles/route.ts +++ b/apps/sim/app/api/tools/iam/list-roles/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listRoles } from '../utils' const logger = createLogger('IAMListRolesAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), pathPrefix: z.string().optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/list-users/route.ts b/apps/sim/app/api/tools/iam/list-users/route.ts index 4e5f7cd5a5..c3ddcf68c7 100644 --- a/apps/sim/app/api/tools/iam/list-users/route.ts +++ b/apps/sim/app/api/tools/iam/list-users/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, listUsers } from '../utils' const logger = createLogger('IAMListUsersAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), pathPrefix: z.string().optional().nullable(), diff --git a/apps/sim/app/api/tools/iam/remove-user-from-group/route.ts b/apps/sim/app/api/tools/iam/remove-user-from-group/route.ts index 0cc3ccdc29..cf149ea77c 100644 --- a/apps/sim/app/api/tools/iam/remove-user-from-group/route.ts +++ b/apps/sim/app/api/tools/iam/remove-user-from-group/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, removeUserFromGroup } from '../utils' const logger = createLogger('IAMRemoveUserFromGroupAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), userName: z.string().min(1, 'User name is required'), diff --git a/apps/sim/app/api/tools/iam/simulate-principal-policy/route.ts b/apps/sim/app/api/tools/iam/simulate-principal-policy/route.ts index 836881fbc0..8744baa639 100644 --- a/apps/sim/app/api/tools/iam/simulate-principal-policy/route.ts +++ b/apps/sim/app/api/tools/iam/simulate-principal-policy/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIAMClient, simulatePrincipalPolicy } from '../utils' const logger = createLogger('IAMSimulatePrincipalPolicyAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), policySourceArn: z.string().min(1, 'Policy source ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/check-assignment-deletion-status/route.ts b/apps/sim/app/api/tools/identity-center/check-assignment-deletion-status/route.ts index 03ac550b77..b9493c4ecf 100644 --- a/apps/sim/app/api/tools/identity-center/check-assignment-deletion-status/route.ts +++ b/apps/sim/app/api/tools/identity-center/check-assignment-deletion-status/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { checkAssignmentDeletionStatus, createSSOAdminClient } from '../utils' const logger = createLogger('IdentityCenterCheckAssignmentDeletionStatusAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/check-assignment-status/route.ts b/apps/sim/app/api/tools/identity-center/check-assignment-status/route.ts index 3de196f977..964fbdccde 100644 --- a/apps/sim/app/api/tools/identity-center/check-assignment-status/route.ts +++ b/apps/sim/app/api/tools/identity-center/check-assignment-status/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { checkAssignmentCreationStatus, createSSOAdminClient } from '../utils' const logger = createLogger('IdentityCenterCheckAssignmentStatusAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/create-account-assignment/route.ts b/apps/sim/app/api/tools/identity-center/create-account-assignment/route.ts index 1cc0e31266..cf0205b811 100644 --- a/apps/sim/app/api/tools/identity-center/create-account-assignment/route.ts +++ b/apps/sim/app/api/tools/identity-center/create-account-assignment/route.ts @@ -8,13 +8,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSSOAdminClient, mapAssignmentStatus } from '../utils' const logger = createLogger('IdentityCenterCreateAccountAssignmentAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/delete-account-assignment/route.ts b/apps/sim/app/api/tools/identity-center/delete-account-assignment/route.ts index 8f15ba2dc1..2ab887e83b 100644 --- a/apps/sim/app/api/tools/identity-center/delete-account-assignment/route.ts +++ b/apps/sim/app/api/tools/identity-center/delete-account-assignment/route.ts @@ -8,13 +8,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSSOAdminClient, mapAssignmentStatus } from '../utils' const logger = createLogger('IdentityCenterDeleteAccountAssignmentAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/describe-account/route.ts b/apps/sim/app/api/tools/identity-center/describe-account/route.ts index 4a4b213d26..fc67bc4729 100644 --- a/apps/sim/app/api/tools/identity-center/describe-account/route.ts +++ b/apps/sim/app/api/tools/identity-center/describe-account/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createOrganizationsClient, describeAccount } from '../utils' const logger = createLogger('IdentityCenterDescribeAccountAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), accountId: z.string().min(12, 'Account ID must be 12 digits').max(12), diff --git a/apps/sim/app/api/tools/identity-center/get-group/route.ts b/apps/sim/app/api/tools/identity-center/get-group/route.ts index fabdd67afa..f4e7a5d1df 100644 --- a/apps/sim/app/api/tools/identity-center/get-group/route.ts +++ b/apps/sim/app/api/tools/identity-center/get-group/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIdentityStoreClient, getGroupByDisplayName } from '../utils' const logger = createLogger('IdentityCenterGetGroupAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), identityStoreId: z.string().min(1, 'Identity Store ID is required'), diff --git a/apps/sim/app/api/tools/identity-center/get-user/route.ts b/apps/sim/app/api/tools/identity-center/get-user/route.ts index 9023213ac7..d7ab10e9fe 100644 --- a/apps/sim/app/api/tools/identity-center/get-user/route.ts +++ b/apps/sim/app/api/tools/identity-center/get-user/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIdentityStoreClient, getUserByEmail } from '../utils' const logger = createLogger('IdentityCenterGetUserAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), identityStoreId: z.string().min(1, 'Identity Store ID is required'), diff --git a/apps/sim/app/api/tools/identity-center/list-account-assignments/route.ts b/apps/sim/app/api/tools/identity-center/list-account-assignments/route.ts index 770a7e0fb0..bef649fac0 100644 --- a/apps/sim/app/api/tools/identity-center/list-account-assignments/route.ts +++ b/apps/sim/app/api/tools/identity-center/list-account-assignments/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSSOAdminClient, listAccountAssignmentsForPrincipal } from '../utils' const logger = createLogger('IdentityCenterListAccountAssignmentsAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/identity-center/list-accounts/route.ts b/apps/sim/app/api/tools/identity-center/list-accounts/route.ts index f183151642..40ac08033b 100644 --- a/apps/sim/app/api/tools/identity-center/list-accounts/route.ts +++ b/apps/sim/app/api/tools/identity-center/list-accounts/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createOrganizationsClient, listAccounts } from '../utils' const logger = createLogger('IdentityCenterListAccountsAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), maxResults: z.number().min(1).max(20).optional(), diff --git a/apps/sim/app/api/tools/identity-center/list-groups/route.ts b/apps/sim/app/api/tools/identity-center/list-groups/route.ts index e490f51dd2..321ab4cec9 100644 --- a/apps/sim/app/api/tools/identity-center/list-groups/route.ts +++ b/apps/sim/app/api/tools/identity-center/list-groups/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createIdentityStoreClient, listGroups } from '../utils' const logger = createLogger('IdentityCenterListGroupsAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), identityStoreId: z.string().min(1, 'Identity Store ID is required'), diff --git a/apps/sim/app/api/tools/identity-center/list-instances/route.ts b/apps/sim/app/api/tools/identity-center/list-instances/route.ts index 88fb4428af..645044718c 100644 --- a/apps/sim/app/api/tools/identity-center/list-instances/route.ts +++ b/apps/sim/app/api/tools/identity-center/list-instances/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSSOAdminClient, listInstances } from '../utils' const logger = createLogger('IdentityCenterListInstancesAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), maxResults: z.number().min(1).max(100).optional(), diff --git a/apps/sim/app/api/tools/identity-center/list-permission-sets/route.ts b/apps/sim/app/api/tools/identity-center/list-permission-sets/route.ts index a3c4388ab6..72a07e2002 100644 --- a/apps/sim/app/api/tools/identity-center/list-permission-sets/route.ts +++ b/apps/sim/app/api/tools/identity-center/list-permission-sets/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSSOAdminClient, listPermissionSets } from '../utils' const logger = createLogger('IdentityCenterListPermissionSetsAPI') const Schema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), instanceArn: z.string().min(1, 'Instance ARN is required'), diff --git a/apps/sim/app/api/tools/ses/create-template/route.ts b/apps/sim/app/api/tools/ses/create-template/route.ts index dd2c7f40d8..1632d274f3 100644 --- a/apps/sim/app/api/tools/ses/create-template/route.ts +++ b/apps/sim/app/api/tools/ses/create-template/route.ts @@ -3,6 +3,7 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, createTemplate } from '../utils' @@ -10,7 +11,12 @@ const logger = createLogger('SESCreateTemplateAPI') const CreateTemplateSchema = z .object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), templateName: z.string().min(1, 'Template name is required'), diff --git a/apps/sim/app/api/tools/ses/delete-template/route.ts b/apps/sim/app/api/tools/ses/delete-template/route.ts index 937eab8cd1..fe81de2f28 100644 --- a/apps/sim/app/api/tools/ses/delete-template/route.ts +++ b/apps/sim/app/api/tools/ses/delete-template/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, deleteTemplate } from '../utils' const logger = createLogger('SESDeleteTemplateAPI') const DeleteTemplateSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), templateName: z.string().min(1, 'Template name is required'), diff --git a/apps/sim/app/api/tools/ses/get-account/route.ts b/apps/sim/app/api/tools/ses/get-account/route.ts index bf637654fa..71f310ed29 100644 --- a/apps/sim/app/api/tools/ses/get-account/route.ts +++ b/apps/sim/app/api/tools/ses/get-account/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, getAccount } from '../utils' const logger = createLogger('SESGetAccountAPI') const GetAccountSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), }) diff --git a/apps/sim/app/api/tools/ses/get-template/route.ts b/apps/sim/app/api/tools/ses/get-template/route.ts index 2dbf7ffe52..4d7c4b687b 100644 --- a/apps/sim/app/api/tools/ses/get-template/route.ts +++ b/apps/sim/app/api/tools/ses/get-template/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, getTemplate } from '../utils' const logger = createLogger('SESGetTemplateAPI') const GetTemplateSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), templateName: z.string().min(1, 'Template name is required'), diff --git a/apps/sim/app/api/tools/ses/list-identities/route.ts b/apps/sim/app/api/tools/ses/list-identities/route.ts index 94c07af853..caac028d66 100644 --- a/apps/sim/app/api/tools/ses/list-identities/route.ts +++ b/apps/sim/app/api/tools/ses/list-identities/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, listIdentities } from '../utils' const logger = createLogger('SESListIdentitiesAPI') const ListIdentitiesSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), pageSize: z.number().int().min(0).max(1000).nullish(), diff --git a/apps/sim/app/api/tools/ses/list-templates/route.ts b/apps/sim/app/api/tools/ses/list-templates/route.ts index 327153a28a..52bcf00cb2 100644 --- a/apps/sim/app/api/tools/ses/list-templates/route.ts +++ b/apps/sim/app/api/tools/ses/list-templates/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, listTemplates } from '../utils' const logger = createLogger('SESListTemplatesAPI') const ListTemplatesSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), pageSize: z.number().int().min(1).max(100).nullish(), diff --git a/apps/sim/app/api/tools/ses/send-bulk-email/route.ts b/apps/sim/app/api/tools/ses/send-bulk-email/route.ts index 415e5fe806..40b357a45c 100644 --- a/apps/sim/app/api/tools/ses/send-bulk-email/route.ts +++ b/apps/sim/app/api/tools/ses/send-bulk-email/route.ts @@ -3,6 +3,7 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, sendBulkEmail } from '../utils' @@ -14,7 +15,12 @@ const DestinationSchema = z.object({ }) const SendBulkEmailSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), fromAddress: z.string().email('Valid sender email address is required'), diff --git a/apps/sim/app/api/tools/ses/send-email/route.ts b/apps/sim/app/api/tools/ses/send-email/route.ts index fdfea2f032..5328bf0d74 100644 --- a/apps/sim/app/api/tools/ses/send-email/route.ts +++ b/apps/sim/app/api/tools/ses/send-email/route.ts @@ -3,6 +3,7 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, sendEmail } from '../utils' @@ -10,7 +11,12 @@ const logger = createLogger('SESSendEmailAPI') const SendEmailSchema = z .object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), fromAddress: z.string().email('Valid sender email address is required'), diff --git a/apps/sim/app/api/tools/ses/send-templated-email/route.ts b/apps/sim/app/api/tools/ses/send-templated-email/route.ts index 391b5ae2e2..0efc9cf5ce 100644 --- a/apps/sim/app/api/tools/ses/send-templated-email/route.ts +++ b/apps/sim/app/api/tools/ses/send-templated-email/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSESClient, sendTemplatedEmail } from '../utils' const logger = createLogger('SESSendTemplatedEmailAPI') const SendTemplatedEmailSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), fromAddress: z.string().email('Valid sender email address is required'), diff --git a/apps/sim/app/api/tools/sts/assume-role/route.ts b/apps/sim/app/api/tools/sts/assume-role/route.ts index f5d2ffb611..fb3cf6c31e 100644 --- a/apps/sim/app/api/tools/sts/assume-role/route.ts +++ b/apps/sim/app/api/tools/sts/assume-role/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { assumeRole, createSTSClient } from '../utils' const logger = createLogger('STSAssumeRoleAPI') const AssumeRoleSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), roleArn: z.string().min(1, 'Role ARN is required'), diff --git a/apps/sim/app/api/tools/sts/get-access-key-info/route.ts b/apps/sim/app/api/tools/sts/get-access-key-info/route.ts index 5ecc88ea10..b2fdcd697b 100644 --- a/apps/sim/app/api/tools/sts/get-access-key-info/route.ts +++ b/apps/sim/app/api/tools/sts/get-access-key-info/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSTSClient, getAccessKeyInfo } from '../utils' const logger = createLogger('STSGetAccessKeyInfoAPI') const GetAccessKeyInfoSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), targetAccessKeyId: z.string().min(1, 'Target access key ID is required'), diff --git a/apps/sim/app/api/tools/sts/get-caller-identity/route.ts b/apps/sim/app/api/tools/sts/get-caller-identity/route.ts index c3bd1cab5b..bec49f9ebc 100644 --- a/apps/sim/app/api/tools/sts/get-caller-identity/route.ts +++ b/apps/sim/app/api/tools/sts/get-caller-identity/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSTSClient, getCallerIdentity } from '../utils' const logger = createLogger('STSGetCallerIdentityAPI') const GetCallerIdentitySchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), }) diff --git a/apps/sim/app/api/tools/sts/get-session-token/route.ts b/apps/sim/app/api/tools/sts/get-session-token/route.ts index 8935efe859..4b7a39bcd1 100644 --- a/apps/sim/app/api/tools/sts/get-session-token/route.ts +++ b/apps/sim/app/api/tools/sts/get-session-token/route.ts @@ -3,13 +3,19 @@ import { toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkInternalAuth } from '@/lib/auth/hybrid' +import { validateAwsRegion } from '@/lib/core/security/input-validation' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { createSTSClient, getSessionToken } from '../utils' const logger = createLogger('STSGetSessionTokenAPI') const GetSessionTokenSchema = z.object({ - region: z.string().min(1, 'AWS region is required'), + region: z + .string() + .min(1, 'AWS region is required') + .refine((v) => validateAwsRegion(v).isValid, { + message: 'Invalid AWS region format (e.g., us-east-1, eu-west-2)', + }), accessKeyId: z.string().min(1, 'AWS access key ID is required'), secretAccessKey: z.string().min(1, 'AWS secret access key is required'), durationSeconds: z.number().int().min(900).max(129600).nullish(), diff --git a/apps/sim/lib/core/security/input-validation.test.ts b/apps/sim/lib/core/security/input-validation.test.ts index fb8dbf35d3..64bf5e33fd 100644 --- a/apps/sim/lib/core/security/input-validation.test.ts +++ b/apps/sim/lib/core/security/input-validation.test.ts @@ -1311,12 +1311,31 @@ describe('validateAwsRegion', () => { expect(result.isValid).toBe(true) }) + it.concurrent('should accept us-iso-west-1', () => { + const result = validateAwsRegion('us-iso-west-1') + expect(result.isValid).toBe(true) + }) + it.concurrent('should accept us-isob-east-1', () => { const result = validateAwsRegion('us-isob-east-1') expect(result.isValid).toBe(true) }) }) + describe('valid Mexico regions', () => { + it.concurrent('should accept mx-central-1', () => { + const result = validateAwsRegion('mx-central-1') + expect(result.isValid).toBe(true) + }) + }) + + describe('valid EU Sovereign Cloud regions', () => { + it.concurrent('should accept eu-isoe-west-1', () => { + const result = validateAwsRegion('eu-isoe-west-1') + expect(result.isValid).toBe(true) + }) + }) + describe('invalid regions', () => { it.concurrent('should reject null', () => { const result = validateAwsRegion(null) diff --git a/apps/sim/lib/core/security/input-validation.ts b/apps/sim/lib/core/security/input-validation.ts index 355bab8c28..12591aeb25 100644 --- a/apps/sim/lib/core/security/input-validation.ts +++ b/apps/sim/lib/core/security/input-validation.ts @@ -857,7 +857,9 @@ export function validateAirtableId( * - GovCloud: us-gov-east-1, us-gov-west-1 * - China: cn-north-1, cn-northwest-1 * - Israel: il-central-1 - * - ISO partitions: us-iso-east-1, us-isob-east-1 + * - ISO partitions: us-iso-east-1, us-iso-west-1, us-isob-east-1 + * - Mexico: mx-central-1 + * - EU Sovereign Cloud: eu-isoe-west-1 * * @param value - The AWS region to validate * @param paramName - Name of the parameter for error messages @@ -883,7 +885,7 @@ export function validateAwsRegion( } const awsRegionPattern = - /^(af|ap|ca|cn|eu|il|me|sa|us|us-gov|us-iso|us-isob)-(central|north|northeast|northwest|south|southeast|southwest|east|west)-\d{1,2}$/ + /^(eu-isoe|us-isob|us-iso|us-gov|af|ap|ca|cn|eu|il|me|mx|sa|us)-(central|north|northeast|northwest|south|southeast|southwest|east|west)-\d{1,2}$/ if (!awsRegionPattern.test(value)) { logger.warn('Invalid AWS region format', {