## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| browser → API (`/modules/cert-manager/analyze`, `build`, `fetch-issuer`) | untrusted caller with a valid session; files, ZIPs, PEM text, passwords, chosen formats and every PEM sent back are untrusted |
| uploaded containers → parsers | ZIP, ASN.1, PKCS#7, PKCS#12, keys and CSRs from unknown vendors or attackers; parsers run in the API process |
| API → internet (AIA caIssuers fetch) | outbound GET to an address named inside an uploaded certificate; answer untrusted |
| API → browser | analysis answers carry unencrypted private keys of the user's own upload (needed for stateless build) |
| repository fixtures | committed test-only private keys |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-ikt-01 | Denial of Service | ZIP expansion (zip bomb, many entries, nesting) | high | mitigate | D-17: magic-byte detection, ≤ 100 entries, ≤ 1 MiB declared per entry, ratio ≤ 100, total ≤ 20 MiB checked before any inflate, one level only, encrypted entries skipped; adm-zip bounds inflation to the declared size; specs with injected limits |
| T-ikt-02 | Denial of Service | multipart upload size and build body | medium | mitigate | 30 files × 5 MiB (multer), total ≤ 20 MiB → 413 `tooLarge`, web refuses over 10 MiB before upload; `build` has its own JSON limit of 512 KiB just above the DTO maximum (≈ 384 kB) with a coded 413 beyond (D-26), every other route keeps 100 kB; e2e checks 380 kB → 400 with code, over 600 KiB → 413 tooLarge, login with 150 kB → 413 |
| T-ikt-17 | Denial of Service | build body parser registration in `main.ts` | medium | mitigate | the wrapper is named `certBuildJsonBody`, never `jsonParser`, because Nest would otherwise skip its global JSON parser for every route (D-26); the spec asserts the name, the JSON login in every e2e setup proves the global parser still runs |
| T-ikt-03 | Denial of Service | malformed ASN.1 / PEM / PKCS#12 | medium | mitigate | every detector in try/catch, bad blobs become `ignored`, never a 500; specs with random, truncated and broken input; PKCS#12 only for a SEQUENCE starting with INTEGER 3; at most 10 distinct passwords tried |
| T-ikt-04 | Tampering / SSRF | AIA fetch | high | mitigate | D-22: URL derived on the server from the uploaded certificate (DTO accepts only `pem`), http/https, default ports, no credentials, `isPublicHttpUrl` before the first request and every redirect (max 3), guarded connect lookup against DNS rebinding, 8 s, 256 KiB, no cookies or auth headers; specs per case; e2e proves 127.0.0.1 and extra `url` field are refused |
| T-ikt-05 | Tampering / SSRF | shared guard bypass via IPv6 spellings | high | mitigate | D-10 hardening with full IPv6 expansion (hex IPv4-mapped, IPv4-compatible, NAT64 incl. local-use, 6to4, Teredo, link/site-local, documentation, discard, zone ids) and a new spec; favorites and nextcloud-status specs re-run |
| T-ikt-06 | Spoofing | fetched certificate injected as issuer | medium | mitigate | only certificates with `checkIssued` AND `verify` against the incomplete certificate are returned; entry marked „nachgeladen von {host}“ |
| T-ikt-07 | Spoofing | wrong chain order or decoy CA from the browser | medium | mitigate | `build` re-runs `buildChains` on every call; a same-name CA with another key fails the signature check (spec with the decoy fixture); certificates outside the primary chain are dropped |
| T-ikt-08 | Information Disclosure | private keys and passwords | high | mitigate | D-11: nothing persisted, working set only in browser memory, passwords only in multipart/JSON bodies (never URLs), no logger call with bodies (request-log logs path and status only), errors carry codes only; e2e greps the api log for passwords, `PRIVATE KEY` and `BEGIN CERTIFICATE` |
| T-ikt-09 | Information Disclosure | AIA failure logging | low | mitigate | one warn line with host and code only, never PEM or URL path; spec asserts it |
| T-ikt-10 | Tampering | scoped forge patch in PFX writing | medium | mitigate | synchronous single call, originals restored in `finally`; spec asserts restoration after success and after an injected throw |
| T-ikt-11 | Elevation of Privilege | route access | medium | mitigate | class `@UseModule('cert-manager')` plus global JwtAuthGuard/TenantGuard as before; controller spec checks class metadata and the exact handler list; old routes removed (e2e: parse → 404) |
| T-ikt-12 | Tampering | file and friendly names from uploads | low | mitigate | names used for display only, never written to disk, control characters removed, max 255; download names and PFX friendlyName through `safeBaseName` |
| T-ikt-13 | Tampering (XSS) | subject strings, SANs, snippets rendered in the browser | low | mitigate | React text only, no `dangerouslySetInnerHTML`; snippet in a `` as text; clipboard gets plain text |
| T-ikt-14 | Denial of Service / Repudiation | AIA as a blind request trigger | low | accept | authenticated module users only, one GET per click to a public address on 80/443, answer only returned when it is a verified issuer certificate; noted in the header comment |
| T-ikt-15 | Information Disclosure | weak PFX encryption (3DES default) | low | accept | needed for older Windows servers (user decision D-07), „Modern (AES-256)“ selectable, guide explains the choice |
| T-ikt-16 | Information Disclosure | committed test private keys | low | accept | test-only PKI generated for this repo, CA keys never committed, README marks the folder for a future secret-scanner allow-list |
| T-ikt-SC | Tampering | npm/pip/cargo installs | low | accept | no new packages (research audit: all libraries already installed; adm-zip flagged SUS only for a recent release date and stays unchanged, no install) |