diff --git a/server/src/__tests__/clips-routes.test.ts b/server/src/__tests__/clips-routes.test.ts index b25aaf8c0a9..6f34ed13ce6 100644 --- a/server/src/__tests__/clips-routes.test.ts +++ b/server/src/__tests__/clips-routes.test.ts @@ -168,6 +168,59 @@ describe("clip routes", () => { vi.clearAllMocks(); }); + it("normalizes public publish moderation and trust states before persistence", async () => { + const companyId = "11111111-1111-4111-8111-111111111111"; + const clipId = "22222222-2222-4222-8222-222222222222"; + const revisionId = "33333333-3333-4333-8333-333333333333"; + mockClipService.publish.mockResolvedValue({ + clip: { + id: clipId, + slug: "support-triage", + visibility: "public", + status: "pending_review", + }, + revision: { + id: revisionId, + revisionNumber: 1, + }, + creatorProfile: { + id: "44444444-4444-4444-8444-444444444444", + }, + }); + + const res = await request(createApp()) + .post(`/api/companies/${companyId}/clips/publish`) + .send({ + creatorProfile: { + handle: "support-team", + displayName: "Support Team", + }, + slug: "support-triage", + type: "bundle", + title: "Support Triage", + summary: "Routes support tickets.", + visibility: "public", + status: "published", + revision: { + manifestChecksum: HASH, + artifactChecksum: HASH, + manifestPayload: { ok: true }, + securityReviewState: "security_reviewed", + verificationState: "passed", + }, + }); + + expect(res.status).toBe(201); + expect(mockClipService.publish).toHaveBeenCalledWith(companyId, expect.objectContaining({ + visibility: "public", + status: "pending_review", + revision: expect.objectContaining({ + securityReviewState: "unreviewed", + verificationState: "not_run", + }), + })); + }); + it("rejects malformed companyId path params before share-preview database lookups", async () => { const res = await request(createApp()) .post("/api/companies/random-company-id/clips/share-preview") diff --git a/server/src/routes/clips.ts b/server/src/routes/clips.ts index 3c85b6f58e9..39ecd99e2c5 100644 --- a/server/src/routes/clips.ts +++ b/server/src/routes/clips.ts @@ -510,7 +510,16 @@ export function clipRoutes(db: Db, storage?: StorageService) { const companyId = getClipCompanyIdParam(req); assertCompanyAccess(req, companyId); if (!consumeClipRateLimit(req, res, "publish")) return; - const result = await svc.publish(companyId, req.body); + const publishInput = { + ...req.body, + status: req.body.visibility === "public" ? "pending_review" as const : req.body.status, + revision: { + ...req.body.revision, + securityReviewState: "unreviewed" as const, + verificationState: "not_run" as const, + }, + }; + const result = await svc.publish(companyId, publishInput); const actor = getActorInfo(req); await logActivity(db, { companyId,