docs(phase-12.2): add/update security threat verification

This commit is contained in:
Jakub Zych
2026-10-02 22:35:37 +02:00
parent ce7003bcf6
commit 6bfc0faa8a

View File

@@ -0,0 +1,97 @@
---
phase: "12.2"
slug: "admin-form-fields-date-file-upload-relation-editing-with-def"
status: verified
# Count of OPEN threats at or above workflow.security_block_on (high).
threats_open: 0
asvs_level: 1
created: "2026-10-02"
verified: "2026-10-02"
---
# Phase 12.2 — Security
> Canonical threat-verification ledger for datepicker, file upload, relation child editing, and deferred binding.
## Trust Boundaries
| Boundary | Description | Data Crossing |
|----------|-------------|---------------|
| Browser → admin API | Authenticated admin form, upload, file, and relation requests | Session keys, multipart bodies, JSON mutations, child and file identifiers |
| Admin API → database | Parent-scoped CRUD and deferred-binding transactions | Admin identity, morph types, relation and pivot data |
| Admin API → blob storage | Guarded file writes, protected reads, thumbnails, and deferred deletion | Untrusted file bytes and object keys |
| Scheduler → maintenance command | Framework-owned deferred purge schedule | Command name, arguments, retention policy |
| Build inputs → shipped admin | Exact-pinned frontend dependency and generated artifacts | Lockfile integrity, compiled SPA assets |
## Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation / Evidence | Status |
|-----------|----------|-----------|----------|-------------|-----------------------|--------|
| T-12.2-01 | Denial of Service | upload stream | high | mitigate | Bounded peek and `LimitReader(limit+1)` in `modules/lagoon/attach/store.go` | closed |
| T-12.2-02 | Elevation of Privilege | image validation | high | mitigate | MIME sniff, decode, format and pixel ceiling in `modules/lagoon/attach/guard.go` | closed |
| T-12.2-03 | Tampering | blob key | high | mitigate | Basename normalization, validated extension and random disk name in `attach/store.go` | closed |
| T-12.2-04 | Tampering | deferred purge | high | mitigate | `SKIP LOCKED`, unattached predicate and created-envelope checks in `lagoon/purge.go` | closed |
| T-12.2-05 | Tampering | blob deletion | medium | mitigate | Deletes registered through `lagoon.AfterCommit` | closed |
| T-12.2-06 | Spoofing | deferred bindings | high | mitigate | Non-null admin id and admin-scoped key validation/lookups in `lagoon/deferred*.go` | closed |
| T-12.2-07 | Tampering | scheduler | medium | mitigate | Framework entry joins compiled table; exact command and arguments required | closed |
| T-12.2-08 | Denial of Service | Fill text parsing | low | accept | Bounded request strings; only linear standard-library parsers are invoked | closed (accepted) |
| T-12.2-09 | Spoofing | upload session | high | mitigate | Authenticated admin id and controller morph included in binding scope | closed |
| T-12.2-10 | Tampering | deferred commit | high | mitigate | Commit reads declared operation fields/relations for the controller morph only | closed |
| T-12.2-11 | Information Disclosure | file lookup | high | mitigate | Owner/session-scoped query; misses return 404 | closed |
| T-12.2-12 | Elevation of Privilege | protected file response | high | mitigate | Inline image allowlist, attachment fallback, nosniff, private cache, sandbox CSP | closed |
| T-12.2-13 | Information Disclosure | public file routing | medium | mitigate | Protected rows omit URLs; static handler gates on `is_public` | closed |
| T-12.2-14 | Denial of Service | upload route | high | mitigate | `MaxBytesReader`, multipart cap, exactly one part and 413 mapping | closed |
| T-12.2-15 | Tampering | admin writes | high | mitigate | Every new mutation route is wrapped in `requireAjax` | closed |
| T-12.2-16 | Tampering | concurrent binding commit | medium | mitigate | Binding read uses `FOR UPDATE`; applied rows share the save transaction | closed |
| T-12.2-17 | Tampering | date bounds | medium | mitigate | Server rechecks min/max during save with field-level errors | closed |
| T-12.2-18 | Denial of Service | JSON bodies | medium | mitigate | Strict capped decoders for file and relation payloads | closed |
| T-12.2-19 | Information Disclosure / Tampering | child scope | high | mitigate | Child query includes bound-slave, foreign-key or pivot parent predicate | closed |
| T-12.2-20 | Tampering | pivot fields | high | mitigate | Server-owned fields excluded; request keys whitelisted; hook stamping retained | closed |
| T-12.2-21 | Elevation of Privilege | relation toolbar | high | mitigate | Declared toolbar capability checked before route work | closed |
| T-12.2-22 | Tampering | relation candidates | medium | mitigate | Live created bindings excluded; hasMany candidates require an unowned key | closed |
| T-12.2-23 | Information Disclosure | parent visibility | high | mitigate | Saved-parent routes load through `loadRecord` and `FormExtendQuery` | closed |
| T-12.2-24 | Spoofing | unsaved relation session | high | mitigate | Backend admin required; full session/admin/master/relation/slave scope | closed |
| T-12.2-25 | Tampering | deferred relation link | medium | mitigate | Existing-record binds re-enter `linkRelated` eligibility checks | closed |
| T-12.2-26 | Information Disclosure | relation form path | medium | mitigate | `$/` paths restricted to the calling plugin | closed |
| T-12.2-27 | Information Disclosure | nested form types | medium | mitigate | Relation, relation-manager, widget and partial types refused | closed |
| T-12.2-28 | Tampering | hasMany foreign key | high | mitigate | Foreign-key fields cannot be declared and are assigned server-side | closed |
| T-12.2-29 | Spoofing | browser session key | medium | mitigate | 32 random bytes from `crypto.getRandomValues` | closed |
| T-12.2-30 | Elevation of Privilege | XSS | high | mitigate | Text interpolation only; phase hygiene gate rejects raw-HTML sinks | closed |
| T-12.2-31 | Information Disclosure | object URLs | low | mitigate | Protected object URLs tracked and revoked | closed |
| T-12.2-32 | Tampering | XHR upload | high | mitigate | Every upload sets `X-Requested-With` | closed |
| T-12.2-33 | Information Disclosure | session key transport | low | mitigate | Keys travel in headers only; phase gate rejects URL parameters | closed |
| T-12.2-34 | Elevation of Privilege | fixture isolation | low | mitigate | `acme.deferred` remains test-only; gate rejects production references | closed |
| T-12.2-35 | Tampering | security test gate | high | mitigate | Named tests are mandatory; missing or skipped tests fail closed | closed |
| T-12.2-36 | Repudiation | release handoff | medium | mitigate | Blocking-human release checkpoint retained; `v0.1.1` remains user-owned | closed |
| T-12.2-SC (plan 01) | Tampering | dependency installs | low | accept | No Go module or npm dependency added in plan 01 | closed (accepted) |
| T-12.2-SC (plan 02) | Tampering | dependency installs | low | accept | No dependency added; existing pinned generators only | closed (accepted) |
| T-12.2-SC (plan 03) | Tampering | dependency installs | low | accept | No Go module or npm dependency added in plan 03 | closed (accepted) |
| T-12.2-SC (plan 04) | Tampering | `@internationalized/date` | high | mitigate | User-approved exact 3.12.4 pin and committed lock integrity | closed |
| T-12.2-SC (plan 05) | Tampering | dependency installs | low | accept | Tests use already-pinned project dependencies; none added | closed (accepted) |
Detailed line-level evidence and test names remain in `12.2-SECURITY-REVIEW.md`. The independent ASVS L1 audit rechecked every register entry against current implementation on 2026-10-02.
## Accepted Risks Log
| Risk ID | Threat Ref | Rationale | Accepted By | Date |
|---------|------------|-----------|-------------|------|
| AR-12.2-01 | T-12.2-08 | Request bodies already bound the string source, and the only added parsers are linear standard-library date/time parsers. | Phase 12.2 plan decision | 2026-10-02 |
| AR-12.2-02 | T-12.2-SC (plan 01) | No dependency was added; `go.mod` and `go.sum` stayed unchanged. | Phase 12.2 plan decision | 2026-10-02 |
| AR-12.2-03 | T-12.2-SC (plan 02) | No dependency was added; the existing pinned OpenAPI generators only regenerated committed outputs. | Phase 12.2 plan decision | 2026-10-02 |
| AR-12.2-04 | T-12.2-SC (plan 03) | No Go module or npm package was added. | Phase 12.2 plan decision | 2026-10-02 |
| AR-12.2-05 | T-12.2-SC (plan 05) | Tests use already-pinned Vitest, Vue Test Utils, happy-dom, testify, and testcontainers-go dependencies. | Phase 12.2 plan decision | 2026-10-02 |
## Security Audit Trail
| Audit Date | Threats Total | Closed | Open | Run By |
|------------|---------------|--------|------|--------|
| 2026-10-02 | 41 | 41 | 0 | `gsd-security-auditor` (ASVS L1) + execute-phase orchestrator |
## Sign-Off
- [x] All threats have a disposition (mitigate / accept / transfer)
- [x] Accepted risks documented in Accepted Risks Log
- [x] `threats_open: 0` confirmed at the configured `high` blocking threshold
- [x] `status: verified` set in frontmatter
**Approval:** verified 2026-10-02. The nine-stage `scripts/check-phase12.2.sh --all` gate passed during this audit.