fix(security): harden input validation schemas and fix SSR sanitize bypass
- blueprint rolePresets: cap array at 100 items to prevent storage abuse - notification CreateManagedNotification: add .max() on title (500), body (2000), type (100), entityType/entityId (200), link (1000), taskAction (200) - settings: add .max() on all string config fields; add regex allowlist (/^[a-zA-Z0-9._-]+$/) on model name fields (geminiModel, azureDalleDeployment, azureOpenAiDeployment) to prevent path manipulation - sanitizeHtml: fix SSR bypass — server-side branch now strips HTML tags instead of returning the raw string unchanged Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,12 +23,12 @@ const SENSITIVE_FIELDS = new Set([
|
||||
export const settingsUpdateInputSchema = z.object({
|
||||
aiProvider: z.enum(["openai", "azure"]).optional(),
|
||||
azureOpenAiEndpoint: z.string().url().optional().or(z.literal("")),
|
||||
azureOpenAiDeployment: z.string().optional(),
|
||||
azureOpenAiApiKey: z.string().optional(),
|
||||
azureApiVersion: z.string().optional(),
|
||||
azureOpenAiDeployment: z.string().regex(/^[a-zA-Z0-9._-]+$/).max(200).optional(),
|
||||
azureOpenAiApiKey: z.string().max(500).optional(),
|
||||
azureApiVersion: z.string().max(500).optional(),
|
||||
aiMaxCompletionTokens: z.number().int().min(50).max(4000).optional(),
|
||||
aiTemperature: z.number().min(0).max(2).optional(),
|
||||
aiSummaryPrompt: z.string().optional(),
|
||||
aiSummaryPrompt: z.string().max(4000).optional(),
|
||||
scoreWeights: z.object({
|
||||
skillDepth: z.number().min(0).max(1),
|
||||
skillBreadth: z.number().min(0).max(1),
|
||||
@@ -47,21 +47,21 @@ export const settingsUpdateInputSchema = z.object({
|
||||
{ message: "Score weights must sum to 1.0" },
|
||||
).optional(),
|
||||
scoreVisibleRoles: z.array(z.enum(["ADMIN", "MANAGER", "CONTROLLER", "USER", "VIEWER"])).optional(),
|
||||
smtpHost: z.string().optional(),
|
||||
smtpHost: z.string().max(500).optional(),
|
||||
smtpPort: z.number().int().min(1).max(65535).optional(),
|
||||
smtpUser: z.string().optional(),
|
||||
smtpPassword: z.string().optional(),
|
||||
smtpUser: z.string().max(500).optional(),
|
||||
smtpPassword: z.string().max(500).optional(),
|
||||
smtpFrom: z.string().email().optional().or(z.literal("")),
|
||||
smtpTls: z.boolean().optional(),
|
||||
anonymizationEnabled: z.boolean().optional(),
|
||||
anonymizationDomain: z.string().trim().min(1).optional(),
|
||||
anonymizationSeed: z.string().trim().min(1).optional().or(z.literal("")),
|
||||
anonymizationDomain: z.string().trim().min(1).max(500).optional(),
|
||||
anonymizationSeed: z.string().trim().min(1).max(500).optional().or(z.literal("")),
|
||||
anonymizationMode: z.enum(["global"]).optional(),
|
||||
azureDalleDeployment: z.string().optional(),
|
||||
azureDalleDeployment: z.string().regex(/^[a-zA-Z0-9._-]+$/).max(200).optional(),
|
||||
azureDalleEndpoint: z.string().url().optional().or(z.literal("")),
|
||||
azureDalleApiKey: z.string().optional(),
|
||||
geminiApiKey: z.string().optional(),
|
||||
geminiModel: z.string().optional(),
|
||||
azureDalleApiKey: z.string().max(500).optional(),
|
||||
geminiApiKey: z.string().max(500).optional(),
|
||||
geminiModel: z.string().regex(/^[a-zA-Z0-9._-]+$/).max(200).optional(),
|
||||
imageProvider: z.enum(["dalle", "gemini"]).optional(),
|
||||
vacationDefaultDays: z.number().int().min(0).max(365).optional(),
|
||||
timelineUndoMaxSteps: z.number().int().min(1).max(200).optional(),
|
||||
|
||||
Reference in New Issue
Block a user