43 KiB
phase, plan, type, wave, depends_on, quick_id, description, date, files_modified, autonomous, requirements, estimate, must_haves
| phase | plan | type | wave | depends_on | quick_id | description | date | files_modified | autonomous | requirements | estimate | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-261008-h3t | 01 | execute | 1 | 261008-h3t | Domains: Standard-Nameserver aus dem AutoDNS-Benutzerprofil statt aus einer Tessera-Einstellung | 2026-10-08 |
|
true |
|
|
|
Locked requirements from the request (cited below as D-xx; numbering follows the request):
- D-01 Remove the settings card "Standard-Nameserver" (web SettingsTab, API DTO/service, validation). DB column: removed by migration (chosen in Task 3; values are worthless).
- D-02 On registration, read the standard nameservers from the AutoDNS user profile (GET /user/{name}/{context}/profile, response UserProfileViews with profiles[] of key/value). The key names are UNKNOWN: recognise tolerantly (keys containing "ns" plus a number, sorted by that number), encapsulated in one function with tests, assumption documented as [ASSUMED] and put on the demo checklist.
- D-03 Registrieren tab and summary show the AutoDNS nameservers read-only, in AutoDNS order. They are stored with the draft (part of what the user confirms; WR-02 version binding stays intact) and sent explicitly in that order in POST /domain.
- D-04 If Tessera cannot read nameservers from AutoDNS (error or no matching keys): clear German Sie-form hint starting "In AutoDNS sind keine Standard-Nameserver hinterlegt …" and registration locked — nothing guessed.
- D-05 Update docs/anleitung-anwender.md, docs/anleitung-administration.md and the existing Domains lines under "Unveröffentlicht" in CHANGELOG.md (adapt, no new line). Do NOT change the 261008-dts SUMMARY.
- Money safety (atomic claim, exactly one POST, UNKNOWN) is not touched except where needed; all existing tests stay green.
Discretion choices (documented here, cited in tasks):
- On a read ERROR (auth, timeout, gateway) the hint says "Tessera konnte die Standard-Nameserver nicht aus AutoDNS lesen …" plus the AutoDNS detail, because "no nameservers stored" would be false in that case; both cases lock registration identically. The "no matching keys" case uses the requested opening sentence verbatim.
- The server reads the profile itself inside createOrder (authoritative); the browser list is informational. The client never sends nameservers.
- Fewer than two recognised nameservers counts as "not stored" (.de needs at least two). No upper cap and no truncation (truncating would be guessing); AutoDNS validates the rest.
Purpose: AutoDNS stays the single source of the nameserver defaults; Tessera holds no company-specific values and never registers with nameservers the user did not see. Output: profile parser with tests, API route GET name-servers, draft/summary/submit using the AutoDNS list, read-only Registrieren display with lock, setting removed including DB column, docs and changelog updated.
<execution_context>
@/.claude/gsd-core/workflows/execute-plan.md
@/.claude/gsd-core/templates/summary.md
</execution_context>
Project rules that apply here:
- NestJS: static routes before any
:idroute (domains.controller.spec.ts checks declaration order). - API TS lib has no Object.hasOwn; use
Object.prototype.hasOwnProperty.callorin. - Never read .env files. Rebuild locally with
docker compose up -d --build api web(the api container runsprisma migrate deployon start). No deploy to the test server, no push (commits stay local; the user pushes bundled). - UI texts: Sie-form, real umlauts (apps/web/src/messages/umlaut-guard.spec.ts fails on ae/oe/ue substitutes), de/en keys identical, no "Mandant"/"Lizenz".
- Global ValidationPipe:
whitelist: true, no forbidNonWhitelisted — unknown body fields are stripped, not rejected.
AutoDNS spec facts (Swagger 2.0, already checked by the planner; the local copy is a session scratch file the executor need not open):
- GET /user/{name}/{context}/profile (operationId userProfileInfo, task 1301017) -> JsonResponseDataUserProfileViews:
datais an array of UserProfileViews{ profiles: UserProfileView[] }; UserProfileView haskey(string, example "techc"),value(string),flag,inherited(bool),readonly(bool), created/updated/owner/updater. - Domain has
nameServers[](objects withname),nameServerGroup,zone. The existing POST /domain body already sendsnameServers: [{ name }]. - Base URLs (autodns-client.ts AUTODNS_BASE_URLS): Demo
https://api.demo.autodns.com/v1, Livehttps://api.autodns.com/v1.autodnsRequestrejects paths containing..,?,#,//or whitespace by throwing.
-
domain-name.ts: move the hostname regex constant HOSTNAME_PATTERN here unchanged and export it. In domains-settings.service.ts delete its local definition and import it from './domain-name' (its own nameserver normaliser stays until Task 3). In domains-orders.service.ts import it from './domain-name' instead of the settings service. autodns-parse.ts imports it from './domain-name' too (no dependency from the parser on a Nest service).
-
autodns-parse.ts (D-02): add exported parseProfileNameServers(data: unknown) returning
{ nameServers: string[]; keys: string[] }. Doc comment in German stating: the key names of the standard nameservers in the AutoDNS user profile are not documented — [ASSUMED], recognised tolerantly, checked by H-1 on the demo checklist of quick 261008-h3t. Rules: a. Collect entries: data may be an array of{ profiles: [...] }(spec), an array of flat{ key, value }items, or one object withprofiles. Keep items whose key and value are strings.keys= distinct trimmed keys in first-seen order, at most 50, each cut to 60 characters. b. Normalise a value: trim, lowercase, remove one trailing dot; accept it only if the whole result matches HOSTNAME_PATTERN (no token splitting for numbered keys). c. Numbered keys: lowercase the key and search anywhere in it for the regex(?:nameserver|name[_-]server|nserver|ns)[_.-]?(\d{1,2})(?!\d); the captured number is the position. Skip entries whose value is not a hostname (for example glue addresses). d. If one position carries two different hostnames, return an empty list (never pick one). The same hostname twice at one position counts once. e. Sort by position numerically, then drop repeated hostnames keeping the first occurrence. f. Only if step c produced no hostname: keys matching exactly^(?:default[_.-]?)?(?:nameservers?|name[_-]servers?|nservers?|ns)$whose value split on[\s,;]+gives tokens that are ALL hostnames provide that list in written order; two such keys with different lists give an empty list. g. No minimum here; the caller enforces it. -
domains-orders.service.ts:
- Add ERR.noDefaultNameServers with code 'noDefaultNameServers' and message "In AutoDNS sind keine Standard-Nameserver hinterlegt. Bitte hinterlegen Sie mindestens zwei Nameserver als Standard in AutoDNS; bis dahin ist keine Registrierung möglich." (thrown as ConflictException, D-04).
- Private readProfileNameServers(credentials): exactly one callRaw GET to the path
/user/+ encodeURIComponent(credentials.user) +/+ credentials.context +/profile— no query, no retry. A thrown error (for example the client's path check) becomes BadGatewayException with code 'autodnsError' and message "Tessera konnte die Standard-Nameserver nicht aus AutoDNS lesen."; a result with ok false is thrown via autodnsFailureToHttp. Parse with parseProfileNameServers; with fewer than MIN_NAMESERVERS (2) entries log one warning via this.logger.warn that starts with "Keine Standard-Nameserver im AutoDNS-Profil erkannt" and lists only the profile key names (never values, never credentials), then throw ERR.noDefaultNameServers. Otherwise return the list. - Public getProfileNameServers(tenantId): getActiveCredentials (409 notConfigured unchanged), then readProfileNameServers; returns
{ environment, nameServers }. - createOrder (D-03): delete the use of the request's nameservers and the method normalizeNameServers plus MAX_NAMESERVERS (no other user). Directly after the existing-order check and before availabilityFor, call readProfileNameServers(credentials) and put the returned ordered list into payload.nameServers. Everything else (availability, contact check, write, summary with version) stays as is; the summary keeps returning payload.nameServers.
- submitOrder: the only change is the existing pre-claim guard — "payload missing" becomes "payload missing OR payload.nameServers.length below MIN_NAMESERVERS", same 400 orderInvalid. Do not touch the claim, the single POST, the body mapping (order preserved, never sorted), outcomeOfSubmit, recoverFailedOutcomeWrite, reconcile or cancelOrder.
- Adjust doc comments that mention nameservers coming from the settings or the browser.
-
dto/domains-order.dto.ts: remove the nameServers field from CreateDomainsOrderDto and the class-validator imports that become unused; doc comment: the nameservers come from AutoDNS, never from the browser. A browser still sending the field is stripped by the whitelist ValidationPipe.
-
domains.controller.ts: add handler getNameServers with
@Get('name-servers')and@ModuleManage('domains'), calling this.orders.getProfileNameServers(this.requireTenantId(req)). Place it directly after checkAvailability in the static section (before every:idroute). No role decorator. Add "Standard-Nameserver aus AutoDNS lesen" to the Verwalten list in the class doc comment. -
Tests:
- autodns-parse.spec.ts: the parser cases from the behavior list, example hostnames only (example.org, example.net, example.com).
- domains-orders.service.spec.ts: extend makeHarness so GET calls whose URL ends with '/profile' are answered by a separate, overridable profile responder (default: envelope with one
{ profiles: [...] }item listing ns3, ns1, ns4, ns2 out of order with example hostnames whose alphabetical order differs from the key order, plus a techc entry); these calls are still recorded in h.calls. Remove nameServers from the createOrder dto fixture and delete the "Nameserver %j -> BadRequest %s" table test (its rules are gone with D-02). Add the service, createOrder, tracer-chain and submit-guard tests from the behavior list. If an existing test counts all calls of a createOrder run, account for the one profile call explicitly rather than weakening the assertion. - domains.controller.spec.ts: add 'getNameServers' to MANAGE_HANDLERS,
getProfileNameServersto makeOrders, the route expectation[0, 'name-servers'], and a forwarding test. - module-manage-handlers.spec.ts: add 'getNameServers' to the DomainsController list only; do not reformat the file (its organizeImports finding is pre-existing).
Commit: feat(domains): Standard-Nameserver aus dem AutoDNS-Profil lesen (h3t).
pnpm --filter @tessera/api exec vitest run src/domains module-manage-handlers rls-coverage rls-access-inventory && pnpm --filter @tessera/api exec tsc --noEmit && test -z "$(grep -nE '^\s*@Roles(' apps/api/src/domains/domains.controller.ts)" && grep -q "@Get('name-servers')" apps/api/src/domains/domains.controller.ts && grep -q "Nameserver aus AutoDNS (h3t)" apps/api/src/domains/domains-orders.service.spec.ts && echo "tracer api ok"
Profile parser covered by tests; createOrder stores the AutoDNS list in AutoDNS order and the chained test proves the one POST /domain carries it unchanged; missing or unreadable profile nameservers block the draft with 409/502/504 and no row; drafts without two nameservers never reach the claim; GET name-servers is a Verwalten route before all :id routes; every existing domains test is green.
-
RegisterTab.tsx (D-03, D-04):
- Remove the editable nameserver list completely: the MIN/MAX constants used only for it, initialNameServers, the nameServers state seeded from the status prop, the inputs and the add/remove buttons. resetAll no longer touches nameservers.
- New state for the AutoDNS list, a small union: loading, ok with list, missing, unreadable with detail. Load via getNameServers on mount and whenever status.environment changes, guarded by an alive flag like the contacts effect. DomainsRequestError with code 'noDefaultNameServers' -> missing; any other error -> unreadable with detail = the DomainsRequestError message, else tc('requestFailed').
- When missing or unreadable, render one hint with role="alert" at the top of the input view (above the domain section, so it is visible before anything is filled in): t('nameServersMissing') or t('nameServersUnreadable') followed by the detail line, plus a SECONDARY_BUTTON t('nameServersRetry') that reloads. The availability check stays usable.
- The nameserver section keeps its place (shown once the domain is free) and keeps the "Zusammenfassung anzeigen" footer; description t('nameServersDescription'); content: loading -> t('nameServersLoading'); ok -> an ordered list (ol) whose items show t('nameServer', { number }) and the hostname as plain text, nothing editable; missing or unreadable -> t('nameServersBlocked').
- canSummarize additionally requires the ok state with at least two entries.
- onSummary calls createOrder without nameservers.
- The summary row keeps summary.nameServers joined with ", " (server order = AutoDNS order). Update the component doc comment (nameservers come from AutoDNS, read-only).
-
de.json / en.json, block domains.register (identical keys in both; Sie-form; real umlauts):
- intro: "Prüfen Sie, ob eine Domain frei ist, wählen Sie die Kontakte und registrieren Sie die Domain verbindlich bei AutoDNS. Die Nameserver übernimmt Tessera aus AutoDNS."
- nameServersDescription: "Diese Standard-Nameserver sind in AutoDNS hinterlegt. Tessera verwendet sie unverändert und in dieser Reihenfolge; ändern lassen sie sich nur in AutoDNS."
- keep nameServer ("Nameserver {number}"); delete addNameServer and removeNameServer
- add nameServersLoading: "Nameserver werden aus AutoDNS gelesen …"
- add nameServersMissing: "In AutoDNS sind keine Standard-Nameserver hinterlegt. Bitte hinterlegen Sie mindestens zwei Nameserver als Standard in AutoDNS. Bis dahin ist keine Registrierung möglich."
- add nameServersUnreadable: "Tessera konnte die Standard-Nameserver nicht aus AutoDNS lesen. Bis das gelingt, ist keine Registrierung möglich." (discretion choice, see objective)
- add nameServersRetry: "Erneut aus AutoDNS lesen"
- add nameServersBlocked: "Ohne Standard-Nameserver aus AutoDNS ist keine Registrierung möglich – siehe Hinweis oben."
- English counterparts with the same meaning. Do not touch domains.settings.nameServers yet (Task 3).
-
RegisterTab.test.tsx: add a mockNameServers for getNameServers in the existing vi.mock (default resolves the example list from the behavior block); replace the prefill assertion and the "zwischen 2 und 6" test with the behavior cases; keep the status fixture as it is (Task 3 drops its old field).
Commit: feat(domains): Registrieren zeigt Nameserver aus AutoDNS nur zur Kontrolle (h3t).
pnpm --filter @tessera/web exec vitest run modules/domains src/messages && pnpm --filter @tessera/web exec tsc --noEmit && node -e 'const de=require("./apps/web/src/messages/de.json"),en=require("./apps/web/src/messages/en.json");const w=(o,p,r)=>{for(const[k,v]of Object.entries(o||{})){const q=p+"."+k;if(v&&typeof v==="object")w(v,q,r);else r[q]=v}return r};const a=w(de.domains,"domains",{}),b=w(en.domains,"domains",{});if(Object.keys(a).length<40||Object.keys(a).sort().join()!==Object.keys(b).sort().join()){console.error("key mismatch");process.exit(1)}if(!String(a["domains.register.nameServersMissing"]).startsWith("In AutoDNS sind keine Standard-Nameserver hinterlegt")){console.error("hint text");process.exit(1)}for(const v of [...Object.values(a),...Object.values(b)])if(/mandant|tenant|lizenz|licens/i.test(String(v))){console.error("bad text",v);process.exit(1)}console.log("web register ok")'
The Registrieren tab lists the AutoDNS nameservers read-only in AutoDNS order, shows the German hint with a retry button when they are missing or unreadable, keeps "Zusammenfassung anzeigen" disabled in that case, and no longer sends nameservers when creating a draft; de/en keys match; web domains tests and the umlaut guard are green.
-
API (D-01):
- domains-settings.service.ts: remove the nameserver constants, the HOSTNAME_PATTERN import, the field from ConfigRow, normalizeNameServers, the saveSettings branch, getDefaultNameServers (no caller since Task 1) and the field in toSettingsView and getStatus; update the class doc comment (no Standard-Nameserver any more). Keep the remaining two forTenant raw hits (loadRow, saveSettings) unchanged so the access inventory stays correct.
- domains.types.ts: remove the field from DomainsSettingsView and DomainsStatusView.
- dto/domains-settings.dto.ts: remove the field and the class-validator imports that become unused.
- Specs: domains-settings.service.spec.ts drops the fixture fields and the nameserver test and adjusts view expectations; add one assertion that getSettings and getStatus return objects without any nameserver property. dto spec: remove the field from the valid body and from the null list.
-
Web (D-01):
- domains-api.ts: remove the field from DomainsStatus, DomainsSettings and SaveDomainsSettingsInput.
- SettingsTab.tsx: delete NameServersCard, padNameServers, the row constants, its render line and imports that become unused.
- de.json and en.json: delete the block domains.settings.nameServers in both.
- domains-page.test.tsx: drop the fixture fields and the test "Nameserver: speichert die bereinigte Liste"; add a test that the Einstellungen tab renders no "Standard-Nameserver" heading and no element with id domains-nameservers.
- RegisterTab.test.tsx: drop the old field from the status fixture.
-
Docs and changelog (D-05; Sie-form in user docs as before; never name the user's concrete nameserver hostnames anywhere):
- docs/anleitung-administration.md, section "Domains: AutoDNS anbinden": replace the bullet "Standard-Nameserver" with a bullet "Nameserver kommen aus AutoDNS": Tessera has no own nameserver setting; for every registration it reads the standard nameservers from the AutoDNS user profile of the AutoDNS user entered in the settings (also values inherited from a parent user) and uses them unchanged, in the order stored there; store at least two there; they must be set up, because for .de domains the DENIC checks them at registration; if Tessera finds none or cannot read them, the Registrieren tab shows a hint and registration is not possible. In the bullet "Eigener AutoDNS-Benutzer" add that the user must be able to read its own user profile.
- docs/anleitung-anwender.md, "Eine Domain registrieren" step 2: replace the sentence about prefilled nameservers ("zwei bis sechs") with: below the contacts you see, for control only, the nameservers stored as standard in AutoDNS, in the order stored there; they can only be changed in AutoDNS; if none are stored, a hint appears and registration is locked. Step 3 (summary lists Nameserver) stays.
- CHANGELOG.md under "Unveröffentlicht", adapt the existing Domains lines, no new line: in the line "Domains, Anbindung an AutoDNS" delete the closing sentence about Standard-Nameserver being prefilled; in the line "Domains, Registrieren" replace "Kontakte und Nameserver wählen" with "Kontakte wählen; die Nameserver übernimmt Tessera unverändert und in derselben Reihenfolge aus den Standardwerten in AutoDNS (sind dort keine hinterlegt, ist die Registrierung gesperrt)".
- docs/mandantentrennung-zugriffsklassifikation.md line 901 (row domains-settings.service.ts): remove ", Standard-Nameserver" from the description and append "Spalte für Standard-Nameserver mit quick-261008-h3t entfernt (Migration 20261008160000)." Keep the table columns unchanged.
- Do not edit .planning/quick/261008-dts-*/261008-dts-SUMMARY.md.
-
Run
pnpm exec biome checkon the touched source directories apps/api/src/domains and "apps/web/src/app/(portal)/modules/domains" plus apps/web/src/lib/domains-api.ts (not on module-manage-handlers.spec.ts, whose finding is pre-existing) and fix what it reports. -
Rebuild locally:
docker compose up -d --build api web; wait until the api answers on http://localhost:3001 and its log shows the migrations applied without error. Nothing is pushed and nothing is deployed to the test server.
Commit: refactor(domains): Einstellung Standard-Nameserver entfernt, Doku angepasst (h3t).
pnpm --filter @tessera/api test && pnpm --filter @tessera/web test && pnpm --filter @tessera/api exec tsc --noEmit && pnpm --filter @tessera/web exec tsc --noEmit && ! grep -rn defaultNameServers apps/api/src apps/web/src && ! grep -rnE "ns14|ns2.ctl" apps/api/src apps/web/src docs CHANGELOG.md && grep -q 'DROP COLUMN "defaultNameServers"' apps/api/prisma/migrations/20261008160000_domains_drop_default_nameservers/migration.sql && grep -q "Nameserver kommen aus AutoDNS" docs/anleitung-administration.md && grep -q "Standardwerten in AutoDNS" CHANGELOG.md && node -e 'const de=require("./apps/web/src/messages/de.json"),en=require("./apps/web/src/messages/en.json");const w=(o,p,r)=>{for(const[k,v]of Object.entries(o||{})){const q=p+"."+k;if(v&&typeof v==="object")w(v,q,r);else r[q]=v}return r};const a=w(de.domains,"domains",{}),b=w(en.domains,"domains",{});if(Object.keys(a).sort().join()!==Object.keys(b).sort().join()||Object.keys(a).some(k=>k.startsWith("domains.settings.nameServers"))){console.error("keys");process.exit(1)}' && docker compose ps --status running --services | grep -qx api && docker compose ps --status running --services | grep -qx web && test "$(docker compose exec -T db psql -U tessera -d tessera -tAc "SELECT count(*) FROM information_schema.columns WHERE table_name='DomainsConfig' AND column_name='defaultNameServers'")" = "0" && A=$(mktemp) && curl -sf -c "$A" -H 'Content-Type: application/json' -d '{"username":"admin","password":"admin123"}' http://localhost:3001/auth/login >/dev/null && MID=$(curl -sf -b "$A" http://localhost:3001/modules/catalog | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const m=JSON.parse(s).find(x=>x.slug==="domains");if(!m)process.exit(1);process.stdout.write(m.isActiveForTenant?"":m.id)})') && { [ -z "$MID" ] || curl -sf -b "$A" -X POST "http://localhost:3001/modules/$MID/activate" >/dev/null; } && S=$(curl -sf -b "$A" http://localhost:3001/modules/domains/settings) && echo "$S" | grep -q '"hasPassword"' && ! echo "$S" | grep -q defaultNameServers && C=$(curl -s -o /dev/null -w '%{http_code}' -b "$A" http://localhost:3001/modules/domains/name-servers) && case "$C" in 200|409|502|504) echo "final gates ok (name-servers $C)";; *) echo "name-servers answered $C"; exit 1;; esac
End of task, with Demo credentials entered by the user (record as checklist H-1..H-4 in the SUMMARY; not run by the executor): H-1 [ASSUMED] key names — Registrieren tab lists the Demo user's standard nameservers in the AutoDNS order; if instead the hint "In AutoDNS sind keine Standard-Nameserver hinterlegt" appears although AutoDNS has values, read the warning line "Keine Standard-Nameserver im AutoDNS-Profil erkannt" in docker compose logs api (it lists the key names) and adapt parseProfileNameServers. H-2 the AutoDNS user may read its own profile (no 403; a 403 shows the unreadable hint). H-3 a demo registration sends the shown nameservers and AutoDNS accepts them explicitly. H-4 Live: the list matches the four values the user named, in that order. H-x replaces item 8 (A8) of the 261008-dts demo checklist, whose step "Standard-Nameserver in den Einstellungen eintragen" no longer exists.
No "Standard-Nameserver" card in Einstellungen, no nameserver field in settings/status API and web types, column dropped by migration 20261008160000 and absent in the local DB; docs and the existing CHANGELOG Domains lines describe nameservers from AutoDNS; full api and web suites, both tsc runs and biome on touched files are green; the rebuilt stack serves GET name-servers without 404/500.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| browser -> API | Manager-controlled request bodies for draft creation and submit; the nameserver list must not be taken from here |
| API -> AutoDNS | Profile read (new GET) and the existing single POST /domain; AutoDNS answers are untrusted input to the parser |
| API -> logs | New warning line when no nameservers are recognised |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-h3t-01 | Tampering | createOrder request body | high | mitigate | Field removed from CreateDomainsOrderDto (whitelist strips it); server reads the profile itself; test proves a sent list is ignored (Task 1) |
| T-h3t-02 | Tampering | draft vs. confirmed order | high | mitigate | List stored in the draft payload; summary returns it; WR-02 updatedAt binding in the claim unchanged; re-creating a draft re-reads the profile and changes the version (Task 1 test) |
| T-h3t-03 | Tampering | profile path built from the stored AutoDNS user name | medium | mitigate | encodeURIComponent on the user name plus the client's existing path check; a thrown path error becomes 502 autodnsError, never a request to another path (Task 1) |
| T-h3t-04 | Repudiation / integrity | guessing nameservers | high | mitigate | Ambiguous or fewer than two recognised values -> 409 noDefaultNameServers, no draft; submit guard rejects drafts with fewer than two nameservers before the claim; web keeps the summary button disabled (Tasks 1, 2) |
| T-h3t-05 | Information Disclosure | warning log line | low | mitigate | Logs only profile key names (max 50, 60 chars each), never values or credentials (Task 1) |
| T-h3t-06 | Denial of Service | extra AutoDNS calls | low | accept | One profile GET per Registrieren load and per draft, Verwalten only, through the shared 350 ms limiter, no retry |
| T-h3t-07 | Elevation of Privilege | GET name-servers | medium | mitigate | @ModuleManage('domains'), no role decorator, class-level @UseModule; controller spec and module-manage-handlers spec assert it (Task 1) |
| T-h3t-08 | Tampering | money-safety path | critical | mitigate | Claim, single POST, outcome mapping, UNKNOWN recovery, reconcile and cancel untouched; all existing submit/claim/reconcile tests must pass unchanged (Task 1 verify, Task 3 full suite) |
| T-h3t-SC | Tampering | npm/pip/cargo installs | high | accept | No package installs in this plan; nothing to audit |
| </threat_model> |
<success_criteria>
- The setting "Standard-Nameserver" is gone from UI, API, DTOs, types and database (D-01).
- Registration uses only the nameservers AutoDNS holds in the user profile, recognised by one tested function with the [ASSUMED] key rule (D-02).
- Registrieren and the summary show them read-only in AutoDNS order; the draft stores them, the one POST /domain sends them in that order, WR-02 still binds the confirmation (D-03).
- Without readable nameservers, a clear German hint appears and registration is locked in browser and server (D-04).
- Docs and the existing CHANGELOG Domains lines updated; 261008-dts SUMMARY untouched (D-05).
- Money safety unchanged; every pre-existing test still green. </success_criteria>