[PR #2477] [CLOSED] fix: resolve validation bypass creating "Ghost Recipients" (empty email)) #2330

Closed
opened 2026-02-26 20:33:25 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/documenso/documenso/pull/2477
Author: @abdulalim110
Created: 2/11/2026
Status: Closed

Base: mainHead: fix/ghost-recipient-validation-bypass


📝 Commits (2)

  • 793e84a fix(shared): enforce minimum length for recipient email in zod schema
  • 0255a8e fix(editor): implement onBlur revert logic and cleanup validation UI

📊 Changes

4 files changed (+44 additions, -13 deletions)

View changed files

📝 apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx (+19 -0)
📝 packages/lib/client-only/providers/envelope-editor-provider.tsx (+17 -10)
📝 packages/trpc/server/envelope-router/set-envelope-recipients.types.ts (+6 -2)
📝 packages/trpc/server/recipient-router/router.ts (+2 -1)

📄 Description

Description

This PR fixes the "Ghost Recipients" issue where users could bypass frontend validation by clearing a recipient's email, leading to a broken state where the recipient appeared empty in the form but remained valid (or partially valid) in the backend.

The fix implements a "Strict Backend + Self-Healing Frontend" strategy:

  1. Backend: Enforces strict existence checks (.min(1)) in the Zod schema to reject empty strings at the API level.
  2. Frontend: Implements an onBlur reversion mechanism. If a user clears the email field and leaves focus, the UI automatically reverts to the last known valid email from the database, ensuring the form state always matches the source of truth.

Fixes #2475

Changes Made

  • Backend (Zod Schema): Added .min(1, { message: "Email is required" }) to the set-envelope-recipients TRPC router input schema to prevent saving empty email strings.
  • Frontend (Editor Provider): Updated EnvelopeEditorProvider to suppress the generic "Save Failed" toast specifically for this validation error, preventing unnecessary user alarm during the reversion process.
  • Frontend (UI Component): Modified RecipientAutoCompleteInput to include an onBlur handler that checks for empty values and resets the field to the persisted recipient data if invalid.
  • UI/UX: Added a visual required indicator (*) to the Recipient Email label for better clarity and removed redundant inline error messages to reduce UI flickering.

Testing Performed

Scenario 1 (Validation Revert):

  1. Opened a document with existing recipients.
  2. Cleared the email field of a recipient.
  3. Clicked outside the input (trigger onBlur).
  4. Result: The field automatically reverts to the original email. No "Save Failed" toast appears.

Scenario 2 (Backend Safety):

  1. Attempted to send an empty email string via API/Network request.
  2. Result: The backend correctly returns a 400 Bad Request (Validation Error), ensuring no corrupt data is stored.

Scenario 3 (New Signer):

  1. Added a new signer.
  2. Verified that the field remains required and validates correctly before saving.

Checklist

  • I have tested these changes locally and they work as expected.
  • I have added/updated tests that prove the effectiveness of these changes.
  • I have updated the documentation to reflect these changes, if applicable.
  • I have followed the project's coding style guidelines.
  • I have addressed the code review feedback from the previous submission, if applicable.

Additional Notes

The decision to use an onBlur revert mechanism instead of a blocking error state was made to preserve the integrity of the document state across different views (Editor vs. Add Fields). This ensures that we never have a "Ghost" recipient that exists in one view but appears empty in another.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/documenso/documenso/pull/2477 **Author:** [@abdulalim110](https://github.com/abdulalim110) **Created:** 2/11/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/ghost-recipient-validation-bypass` --- ### 📝 Commits (2) - [`793e84a`](https://github.com/documenso/documenso/commit/793e84a973c805b9fb9ef8135f9608453f8f6bca) fix(shared): enforce minimum length for recipient email in zod schema - [`0255a8e`](https://github.com/documenso/documenso/commit/0255a8ea4d205c355eae86e1cf4c2c677c06d549) fix(editor): implement onBlur revert logic and cleanup validation UI ### 📊 Changes **4 files changed** (+44 additions, -13 deletions) <details> <summary>View changed files</summary> 📝 `apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx` (+19 -0) 📝 `packages/lib/client-only/providers/envelope-editor-provider.tsx` (+17 -10) 📝 `packages/trpc/server/envelope-router/set-envelope-recipients.types.ts` (+6 -2) 📝 `packages/trpc/server/recipient-router/router.ts` (+2 -1) </details> ### 📄 Description ## Description This PR fixes the "Ghost Recipients" issue where users could bypass frontend validation by clearing a recipient's email, leading to a broken state where the recipient appeared empty in the form but remained valid (or partially valid) in the backend. The fix implements a **"Strict Backend + Self-Healing Frontend"** strategy: 1. **Backend:** Enforces strict existence checks (`.min(1)`) in the Zod schema to reject empty strings at the API level. 2. **Frontend:** Implements an `onBlur` reversion mechanism. If a user clears the email field and leaves focus, the UI automatically reverts to the last known valid email from the database, ensuring the form state always matches the source of truth. ## Related Issue Fixes #2475 ## Changes Made - **Backend (Zod Schema):** Added `.min(1, { message: "Email is required" })` to the `set-envelope-recipients` TRPC router input schema to prevent saving empty email strings. - **Frontend (Editor Provider):** Updated `EnvelopeEditorProvider` to suppress the generic "Save Failed" toast specifically for this validation error, preventing unnecessary user alarm during the reversion process. - **Frontend (UI Component):** Modified `RecipientAutoCompleteInput` to include an `onBlur` handler that checks for empty values and resets the field to the persisted recipient data if invalid. - **UI/UX:** Added a visual required indicator (`*`) to the Recipient Email label for better clarity and removed redundant inline error messages to reduce UI flickering. ## Testing Performed **Scenario 1 (Validation Revert):** 1. Opened a document with existing recipients. 2. Cleared the email field of a recipient. 3. Clicked outside the input (trigger `onBlur`). 4. **Result:** The field automatically reverts to the original email. No "Save Failed" toast appears. **Scenario 2 (Backend Safety):** 1. Attempted to send an empty email string via API/Network request. 2. **Result:** The backend correctly returns a 400 Bad Request (Validation Error), ensuring no corrupt data is stored. **Scenario 3 (New Signer):** 1. Added a new signer. 2. Verified that the field remains required and validates correctly before saving. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## Additional Notes The decision to use an `onBlur` revert mechanism instead of a blocking error state was made to preserve the integrity of the document state across different views (Editor vs. Add Fields). This ensures that we never have a "Ghost" recipient that exists in one view but appears empty in another. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-02-26 20:33:25 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/documenso#2330
No description provided.