Skip to content

fix: reject invalid SES regions#424

Merged
KMKoushik merged 3 commits into
mainfrom
codex/find-simple-github-issues
Jul 13, 2026
Merged

fix: reject invalid SES regions#424
KMKoushik merged 3 commits into
mainfrom
codex/find-simple-github-issues

Conversation

@KMKoushik

@KMKoushik KMKoushik commented Jul 12, 2026

Copy link
Copy Markdown
Member

Closes #85

What changed

  • validate and normalize SES regions consistently in the form, tRPC inputs, environment configuration, and service layer
  • load AWS_DEFAULT_REGION before mounting the SES settings form, with stable loading and retry states
  • filter blank legacy regions and deduplicate valid regions before rendering the Add Domain selector
  • reject blank regions when creating domains
  • add unit coverage for blank, whitespace, normalization, and legacy-region filtering

Root cause

The SES settings form initialized region as an empty string while displaying us-east-1 only as a placeholder. Both client and server schemas used z.string(), which accepts empty and whitespace-only values. Older versions could persist that invalid setting before AWS provisioning failed. When a valid setting was added later, the Add Domain dialog rendered both regions and passed the blank one to Radix as <SelectItem value="">, causing the client-side exception.

Impact

New blank SES regions are rejected before AWS side effects. Existing installations with a legacy blank row can open Add Domain safely; only normalized, valid region choices are shown. The invalid row remains visible in Admin for deliberate cleanup.

Verification

  • pnpm --filter=web exec vitest run -c vitest.unit.config.ts src/lib/zod/ses-setting-schema.unit.test.ts (3 tests passed)
  • ESLint passed for all changed TypeScript/TSX files
  • apps/web/src/env.js passed ESLint with --quiet
  • no build or database migrations run

Summary by cubic

Reject invalid SES regions and normalize them across the form, API, env, and service to stop blank-region crashes in Add Domain. The default region now loads before the form; region choices are cleaned and deduped so existing installs can open Add Domain safely.

  • Bug Fixes
    • Added shared sesRegionSchema to trim/validate regions and reject blanks; AWS_DEFAULT_REGION is validated inline (trim + required) to keep env loading Node-compatible.
    • Load AWS_DEFAULT_REGION before rendering the SES settings form with loading/retry states.
    • Clean and dedupe region options in Add Domain; enforce schema when creating domains.
    • Handle SES quota lookup failures: show a field error and fall back to a safe default send rate.
    • Unit tests cover normalization, empty values, and legacy-region filtering.

Written for commit c9432f7. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • The SES settings form now loads and preselects the default region, showing a loading state and a Retry option if it fails.
    • Sending quota now updates automatically based on the entered region.
    • Available SES regions are cleaned up, deduplicated, and presented consistently.
  • Bug Fixes

    • SES region values are now trimmed and validated consistently across settings and domain creation, with clearer handling of invalid/blank inputs.
    • Server-side AWS_DEFAULT_REGION validation was tightened to reject blank/whitespace-only values.
  • Tests

    • Added unit tests covering SES region validation and normalization.

Entire-Checkpoint: 26b0394dad5f
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
unsend-marketing Ready Ready Preview, Comment Jul 13, 2026 12:05am

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The change adds shared SES region validation and normalization. The configured default region is exposed through the admin API and loaded by the SES settings form, which handles loading and retry states and validates regions before quota lookup. Environment configuration, admin procedures, domain creation, available-region output, and SES setting creation use the shared schema.

Possibly related PRs

  • usesend/useSend#193: Both changes modify the domain creation path and its data passed to the service layer.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR filters blank legacy regions in the Add Domain flow, preventing empty Select.Item values and addressing the reported crash in #85.
Out of Scope Changes check ✅ Passed The changes stay focused on SES region validation, normalization, and related form/service fixes.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: tightening validation to reject invalid SES regions across the app.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 12, 2026

Copy link
Copy Markdown

Deploying usesend with  Cloudflare Pages  Cloudflare Pages

Latest commit: c9432f7
Status: ✅  Deploy successful!
Preview URL: https://9f12cf78.usesend.pages.dev
Branch Preview URL: https://codex-find-simple-github-iss.usesend.pages.dev

View logs

@KMKoushik
KMKoushik marked this pull request as ready for review July 12, 2026 23:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web/src/server/api/routers/domain.ts (1)

25-37: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add tRPC regression coverage for region normalization.

The helper is unit-tested, but the router wiring is not. Add *.trpc.test.ts cases proving blank legacy regions are omitted, normalized duplicates collapse, and createDomain rejects blank regions while trimming valid input.

As per coding guidelines, API or behavior changes in apps/web require corresponding tests using the *.trpc.test.ts naming convention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/api/routers/domain.ts` around lines 25 - 37, Add
*.trpc.test.ts regression cases for the domain router’s getAvailableRegions and
createDomain procedures: verify blank legacy regions are omitted, normalized
duplicate regions collapse, and createDomain rejects blank regions while
trimming valid input. Exercise the router wiring through tRPC rather than
testing only getValidSesRegions directly.

Source: Coding guidelines

apps/web/src/server/api/routers/admin.ts (1)

71-99: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add tRPC coverage for the new region contract.

The new default-region query and region input schemas change API behavior. Add *.trpc.test.ts cases covering the default value, trimmed regions, and rejection of empty/whitespace regions for both quota lookup and SES-setting creation.

As per coding guidelines, API or behavior changes in apps/web require corresponding tests using the *.trpc.test.ts naming convention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/api/routers/admin.ts` around lines 71 - 99, Add
*.trpc.test.ts coverage for getDefaultSesRegion, getQuotaForRegion, and
addSesSettings. Verify the default-region query returns the configured default,
valid region inputs are trimmed and accepted, and empty or whitespace-only
regions are rejected for both quota lookup and SES-setting creation.

Source: Coding guidelines

apps/web/src/server/service/ses-settings-service.ts (1)

55-70: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add a service-level normalization regression test.

Verify that a padded region passed to createSesSetting is normalized before duplicate checks, persistence, and AWS calls.

As per coding guidelines, behavior changes in apps/web require corresponding unit or integration tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/service/ses-settings-service.ts` around lines 55 - 70,
Add a service-level regression test for createSesSetting that passes a padded
region, verifies it is normalized before the duplicate-cache check, and confirms
the normalized value is used for persistence and AWS calls. Mock the required
initialization and dependencies, and cover the expected normalized-region
behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/components/settings/AddSesSettings.tsx`:
- Around line 52-96: Add compatible unit or integration tests for the
AddSesSettings component and SesSettingsForm covering loading, retry after a
missing default region, rendering the fetched default region, normalized valid
input, invalid input, and a rejected quota request. Reuse existing testing
utilities and API-mocking patterns from the surrounding apps/web tests, and
assert the user-visible outcomes for each state.
- Around line 142-148: Update onRegionInputOutOfFocus around
getQuotaForRegion.fetch to catch rejected quota lookups, including unsupported
regions, and prevent unhandled promises. On failure, surface an appropriate
region/form error or apply the existing safe send-rate fallback while preserving
the successful quota behavior.

In `@apps/web/src/server/service/ses-settings-service.ts`:
- Line 66: Update the getSetting read path to normalize the requested SES region
with the shared sesRegionSchema before using it as a cache key or querying
settings, so whitespace is trimmed and empty input follows the existing
default-region behavior. Keep createSesSetting’s validation unchanged and reuse
the normalized region consistently throughout getSetting.

---

Outside diff comments:
In `@apps/web/src/server/api/routers/admin.ts`:
- Around line 71-99: Add *.trpc.test.ts coverage for getDefaultSesRegion,
getQuotaForRegion, and addSesSettings. Verify the default-region query returns
the configured default, valid region inputs are trimmed and accepted, and empty
or whitespace-only regions are rejected for both quota lookup and SES-setting
creation.

In `@apps/web/src/server/api/routers/domain.ts`:
- Around line 25-37: Add *.trpc.test.ts regression cases for the domain router’s
getAvailableRegions and createDomain procedures: verify blank legacy regions are
omitted, normalized duplicate regions collapse, and createDomain rejects blank
regions while trimming valid input. Exercise the router wiring through tRPC
rather than testing only getValidSesRegions directly.

In `@apps/web/src/server/service/ses-settings-service.ts`:
- Around line 55-70: Add a service-level regression test for createSesSetting
that passes a padded region, verifies it is normalized before the
duplicate-cache check, and confirms the normalized value is used for persistence
and AWS calls. Mock the required initialization and dependencies, and cover the
expected normalized-region behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c6b83478-4cea-46a6-be68-06c2cbfc3614

📥 Commits

Reviewing files that changed from the base of the PR and between e419ff1 and d9414f9.

📒 Files selected for processing (7)
  • apps/web/src/components/settings/AddSesSettings.tsx
  • apps/web/src/env.js
  • apps/web/src/lib/zod/ses-setting-schema.ts
  • apps/web/src/lib/zod/ses-setting-schema.unit.test.ts
  • apps/web/src/server/api/routers/admin.ts
  • apps/web/src/server/api/routers/domain.ts
  • apps/web/src/server/service/ses-settings-service.ts

Comment on lines +52 to +96
const defaultRegion = api.admin.getDefaultSesRegion.useQuery();

if (defaultRegion.isLoading) {
return (
<div className="flex min-h-[500px] items-center justify-center">
<Spinner className="h-5 w-5" />
</div>
);
}

if (!defaultRegion.data) {
return (
<div className="flex min-h-[500px] flex-col items-center justify-center gap-3 text-center">
<p className="text-sm text-muted-foreground" role="alert">
Failed to load the default AWS region.
</p>
<Button
type="button"
variant="outline"
onClick={() => void defaultRegion.refetch()}
>
Retry
</Button>
</div>
);
}

return (
<SesSettingsForm
defaultRegion={defaultRegion.data}
onSuccess={onSuccess}
/>
);
};

const SesSettingsForm: React.FC<
SesSettingsProps & { defaultRegion: string }
> = ({ defaultRegion, onSuccess }) => {
const addSesSettings = api.admin.addSesSettings.useMutation();
const utils = api.useUtils();

const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
region: "",
region: defaultRegion,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add tests for the new settings-form states.

Cover loading, retry, the fetched default region, normalized valid input, invalid input, and a rejected quota request.

As per coding guidelines, behavior changes in apps/web require compatible unit or integration tests.

Also applies to: 141-148

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/settings/AddSesSettings.tsx` around lines 52 - 96,
Add compatible unit or integration tests for the AddSesSettings component and
SesSettingsForm covering loading, retry after a missing default region,
rendering the fetched default region, normalized valid input, invalid input, and
a rejected quota request. Reuse existing testing utilities and API-mocking
patterns from the surrounding apps/web tests, and assert the user-visible
outcomes for each state.

Source: Coding guidelines

Comment thread apps/web/src/components/settings/AddSesSettings.tsx Outdated
Comment thread apps/web/src/server/service/ses-settings-service.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/server/service/ses-settings-service.ts`:
- Around line 31-36: Update invalidateCache() to normalize persisted region keys
with the same trimming/schema rules used by getSetting() and createSesSetting().
Skip blank or invalid regions while rebuilding this.cache, and define
deterministic handling when multiple raw keys normalize to the same region so
getSetting() and duplicate checks use the normalized entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff29bac3-66c3-4b53-9530-35de9f049c48

📥 Commits

Reviewing files that changed from the base of the PR and between d9414f9 and 7e03469.

📒 Files selected for processing (2)
  • apps/web/src/components/settings/AddSesSettings.tsx
  • apps/web/src/server/service/ses-settings-service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/settings/AddSesSettings.tsx

Comment on lines +31 to +36
const normalizedRegion = sesRegionSchema.parse(region);

await this.checkInitialized();

if (this.cache[region]) {
return this.cache[region] as SesSetting;
if (this.cache[normalizedRegion]) {
return this.cache[normalizedRegion] as SesSetting;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Normalize cached settings before using normalized region keys.

getSetting and createSesSetting now use trimmed regions, but invalidateCache() still stores entries under raw persisted values. A legacy " us-east-1 " row therefore cannot be found by getSetting("us-east-1"), and the duplicate check can permit a second setting for the same normalized region. Normalize valid legacy keys while rebuilding this.cache, skip blank entries, and define collision handling.

Also applies to: 68-72

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/service/ses-settings-service.ts` around lines 31 - 36,
Update invalidateCache() to normalize persisted region keys with the same
trimming/schema rules used by getSetting() and createSesSetting(). Skip blank or
invalid regions while rebuilding this.cache, and define deterministic handling
when multiple raw keys normalize to the same region so getSetting() and
duplicate checks use the normalized entry.

@KMKoushik
KMKoushik merged commit c1f898c into main Jul 13, 2026
6 checks passed
@KMKoushik
KMKoushik deleted the codex/find-simple-github-issues branch July 13, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 - Application error: a client-side exception has occurred

1 participant