From 6bfc0faa8abaf9378080b7a2b5f69771ecce0c7e Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Fri, 2 Oct 2026 22:35:37 +0200 Subject: [PATCH] docs(phase-12.2): add/update security threat verification --- .../12.2-SECURITY.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 .planning/phases/12.2-admin-form-fields-date-file-upload-relation-editing-with-def/12.2-SECURITY.md diff --git a/.planning/phases/12.2-admin-form-fields-date-file-upload-relation-editing-with-def/12.2-SECURITY.md b/.planning/phases/12.2-admin-form-fields-date-file-upload-relation-editing-with-def/12.2-SECURITY.md new file mode 100644 index 0000000..d07a64d --- /dev/null +++ b/.planning/phases/12.2-admin-form-fields-date-file-upload-relation-editing-with-def/12.2-SECURITY.md @@ -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.