From e70a1163f3a341f6cf9745b09c31bf83f566b7cb Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:20:40 +0200 Subject: [PATCH] fix: too strict cron expression validation (#29138) --- open-api/immich-openapi-specs.json | 4 ---- server/src/dtos/system-config.dto.ts | 12 +++++++++++- server/src/services/system-config.service.spec.ts | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 014679b310..88c0412b67 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -18490,7 +18490,6 @@ "properties": { "cronExpression": { "description": "Cron expression", - "pattern": "(((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\*) ?){5,7}", "type": "string" }, "enabled": { @@ -25677,7 +25676,6 @@ "properties": { "cronExpression": { "description": "Cron expression for when the integrity check should run", - "pattern": "(((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\*) ?){5,7}", "type": "string" }, "enabled": { @@ -25710,7 +25708,6 @@ "properties": { "cronExpression": { "description": "Cron expression for when the integrity check should run", - "pattern": "(((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\*) ?){5,7}", "type": "string" }, "enabled": { @@ -25810,7 +25807,6 @@ "properties": { "cronExpression": { "description": "Cron expression", - "pattern": "(((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\*) ?){5,7}", "type": "string" }, "enabled": { diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 1f6691c87b..7e1efdc862 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -1,3 +1,4 @@ +import { validateCronExpression } from 'cron'; import { createZodDto } from 'nestjs-zod'; import { SystemConfig } from 'src/config'; import { @@ -43,7 +44,16 @@ const JobSettingsSchema = z const cronExpressionSchema = z .string() - .regex(/(((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7}/, 'Invalid cron expression') + .superRefine((value, ctx) => { + const validated = validateCronExpression(value); + if (!validated.valid) { + ctx.addIssue({ + code: 'custom', + message: `Invalid cron expression. ${validated.error?.message ?? ''}`, + input: value, + }); + } + }) .describe('Cron expression'); const DatabaseBackupSchema = z diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 7b14832155..920b9b9346 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -319,14 +319,14 @@ describe(SystemConfigService.name, () => { it('should accept valid cron expressions', async () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue( - JSON.stringify({ library: { scan: { cronExpression: '0 0 * * *' } } }), + JSON.stringify({ library: { scan: { cronExpression: '0 0 */3 * *' } } }), ); await expect(sut.getSystemConfig()).resolves.toMatchObject({ library: { scan: { enabled: true, - cronExpression: '0 0 * * *', + cronExpression: '0 0 */3 * *', }, }, });