Conversation
## Because - FXA-13596 shipped the domain blocklist panel and the auth-server enforcement, but not a seed path. Today an admin can only paste or upload domains by hand. - The public disposable-domain list holds about 40k entries. Nobody is going to paste that. ## This pull request - Adds `POST /api/domain-blocklist/sync`. It fetches a list URL over https, parses it, and imports it through the existing `DomainBlocklist.addMany()`. - Parses the list line by line. Blank lines and `#` comments are skipped, entries are normalized, invalid domains and repeats are dropped. - Imports in batches of 500 with a 50 ms pause between them. `addMany` ignores conflicts, so a domain that is already blocked does not error. - Adds an editable URL field to the panel, pre-filled with the disposable-email-domains list, plus a sync button and a result line. - Adds `syncDomainBlocklist()` to the panel API client and `DomainBlocklistSyncResult` to the admin-server types. ## Issue that this pull request solves Closes: https://mozilla-hub.atlassian.net/browse/FXA-13674
There was a problem hiding this comment.
Pull request overview
Adds admin-driven domain blocklist synchronization from a remote URL.
Changes:
- Adds HTTPS list fetching, parsing, validation, and batched imports.
- Adds panel controls and API integration for synchronization.
- Adds result types and server/UI tests.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
packages/fxa-admin-server/src/types.ts |
Defines the synchronization result. |
packages/fxa-admin-server/src/rest/domain-blocklist/domain-blocklist.controller.ts |
Implements remote-list synchronization. |
packages/fxa-admin-server/src/rest/domain-blocklist/domain-blocklist.controller.spec.ts |
Tests fetching, parsing, batching, and failures. |
packages/fxa-admin-panel/src/lib/api.ts |
Adds the synchronization API client. |
packages/fxa-admin-panel/src/components/PageDomainBlocklist/index.tsx |
Adds synchronization controls and results. |
packages/fxa-admin-panel/src/components/PageDomainBlocklist/index.test.tsx |
Tests the new panel workflow. |
Suppressed comments (1)
packages/fxa-admin-panel/src/components/PageDomainBlocklist/index.tsx:226
- This asynchronously inserted result has no live-region semantics, so screen-reader users may not hear whether the sync succeeded or failed while focus remains on the button. Mark the message as a status (or use an alert for the error case).
{syncResult && (
<p data-testid="domain-blocklist-sync-result" className="mt-2">
{syncResult}
</p>
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const abort = new AbortController(); | ||
| const timer = setTimeout(() => abort.abort(), SYNC_FETCH_TIMEOUT_MS); | ||
| try { | ||
| const res = await fetch(source.toString(), { signal: abort.signal }); |
| const DEFAULT_SYNC_URL = | ||
| 'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/disposable_email_blocklist.conf'; |
|
🤖 Blocked on a decision, not on a fix. CodeQL flags a critical server-side request forgery at This is inherent to the requirement. FXA-13674 asks for the list URL to be editable in the panel so a different list can be imported manually, and a server that fetches an admin-supplied URL is an SSRF. Three defensible options, and picking one is a human call:
The only other failure is a |
Because
This pull request
POST /api/domain-blocklist/sync. It fetches a list URL over https, parses it, and imports it through the existingDomainBlocklist.addMany().#comments are skipped, entries are normalized, invalid domains and repeats are dropped.addManyignores conflicts, so a domain that is already blocked does not error.syncDomainBlocklist()to the panel API client andDomainBlocklistSyncResultto the admin-server types.Issue that this pull request solves
Closes: https://mozilla-hub.atlassian.net/browse/FXA-13674
Checklist
Put an
xin the boxes that applyHow to review (Optional)
domain-blocklist.controller.ts, and inside itparseSyncUrl(),fetchList()andreadCappedBody().Screenshots (Optional)
None. The panel change is a URL field, a sync button and a one-line result under the existing add form. I could not take a screenshot because the local stack was not running for this change.
Other information (Optional)
domain-blocklist.controller.spec.tsis 27 passed, 0 failed, and the panelPageDomainBlocklist/index.test.tsxis 13 passed, 0 failed.nx lintpasses for both packages.DomainBlocklistfeature guard and the audit log, it accepts https only, and it caps the response body at 10MB.fetchfollows the whole chain before the check can run, so a middle hop over plaintext is still requested.