diff --git a/src/backend/controllers/feedback/AppFeedbackController.test.ts b/src/backend/controllers/feedback/AppFeedbackController.test.ts index 743c7ec15..b92edb3c2 100644 --- a/src/backend/controllers/feedback/AppFeedbackController.test.ts +++ b/src/backend/controllers/feedback/AppFeedbackController.test.ts @@ -357,6 +357,20 @@ describe('AppFeedbackController POST /', () => { ).rejects.toMatchObject({ statusCode: 400 }); }); + it('throws 400 when the origin exceeds the stored column size', async () => { + // source_origin is VARCHAR(2048) on MySQL/Postgres; a longer origin + // must be rejected up front, not fail (or silently truncate) at the + // INSERT after passing every other validation. + mockEmailConfigured(); + const { userId: ownerId } = await makeUser(); + const app = await makeApp(ownerId, { feedbackEnabled: true }); + const origin = `${new URL(app.index_url).origin}/${'x'.repeat(2500)}`; + const { actor } = await makeUser(); + await expect( + submit(actor, { origin, message: 'hi' }), + ).rejects.toMatchObject({ statusCode: 400 }); + }); + it('throws 403 feedback_not_enabled when the app has not opted in', async () => { mockEmailConfigured(); const { userId: ownerId } = await makeUser(); diff --git a/src/backend/controllers/feedback/AppFeedbackController.ts b/src/backend/controllers/feedback/AppFeedbackController.ts index 0fa4cc2f5..2e972ef68 100644 --- a/src/backend/controllers/feedback/AppFeedbackController.ts +++ b/src/backend/controllers/feedback/AppFeedbackController.ts @@ -38,8 +38,16 @@ import { PuterController } from '../types.js'; /** Sanity cap on the raw body field; the service enforces the real limit. */ const RAW_MESSAGE_CAP = 50_000; +// Upper bound on the `app`/`origin` target params. Must not exceed the +// `source_origin` column (VARCHAR(2048) on MySQL/Postgres): the raw origin is +// stored verbatim, and a longer value would fail the INSERT after passing +// every validation — or be silently truncated on non-strict MySQL. +const TARGET_PARAM_MAX_LENGTH = 2048; + const readTargetParam = (value: unknown): string | undefined => { - return typeof value === 'string' && value.length > 0 && value.length <= 3000 + return typeof value === 'string' && + value.length > 0 && + value.length <= TARGET_PARAM_MAX_LENGTH ? value : undefined; };