docs(14.2.1-04): complete translate tests, gate, and security review

Record the last-plan SUMMARY, closed high-threat review, validation sign-off, and 4/4 roadmap progress.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-06 14:23:06 +02:00
parent ba6e4369f6
commit 249b4d10ca
5 changed files with 517 additions and 56 deletions

View File

@@ -830,7 +830,7 @@ Plans:
Plans: Plans:
- [ ] TBD (run $gsd-plan-phase 14.2 to break down) - [ ] TBD (run $gsd-plan-phase 14.2 to break down)
### Phase 14.2.1: Translate plugin (INSERTED) ### Phase 14.2.1: Translate plugin (INSERTED) — complete 2026-10-06
**Goal:** Golem15.Translate is ported to Go as `sm-translate-plugin` so Journal (Phase 15) can keep translatable fields. Lean core only: Locale model and Locales admin, Translatable API, cabana `mltext`/`mlmarkdown`, and PHP Translator locale resolution (URL prefix / session / cookie / default). **Goal:** Golem15.Translate is ported to Go as `sm-translate-plugin` so Journal (Phase 15) can keep translatable fields. Lean core only: Locale model and Locales admin, Translatable API, cabana `mltext`/`mlmarkdown`, and PHP Translator locale resolution (URL prefix / session / cookie / default).
**Requirements**: TBD **Requirements**: TBD
@@ -843,7 +843,7 @@ Plans:
3. Cabana `markdown`/`mltext`/`mlmarkdown` compose; nested locale writes are not dropped; docs, OpenAPI, TS types and `boardwalk` `dist/` update in the same change. 3. Cabana `markdown`/`mltext`/`mlmarkdown` compose; nested locale writes are not dropped; docs, OpenAPI, TS types and `boardwalk` `dist/` update in the same change.
4. Proof host `sm-grzybyfunkcjonalne-app` boots with user+translate; unit tests are the last plan. 4. Proof host `sm-grzybyfunkcjonalne-app` boots with user+translate; unit tests are the last plan.
**Plans:** 3/4 plans executed **Plans:** 4/4 plans executed
Plans: Plans:
@@ -857,7 +857,7 @@ Plans:
- [x] 14.2.1-03-PLAN.md — Cabana markdown/ML fields, nested save, docs/OpenAPI/dist, proof host boot - [x] 14.2.1-03-PLAN.md — Cabana markdown/ML fields, nested save, docs/OpenAPI/dist, proof host boot
**Wave 4** *(blocked on Wave 3 completion)* **Wave 4** *(blocked on Wave 3 completion)*
- [ ] 14.2.1-04-PLAN.md — Unit/integration tests last, phase gate, security review - [x] 14.2.1-04-PLAN.md — Unit/integration tests last, phase gate, security review
### Phase 15: Journal plugin ### Phase 15: Journal plugin

View File

@@ -3,17 +3,17 @@ gsd_state_version: "1.0"
milestone: v1.0 milestone: v1.0
current_phase: 14.2.1 current_phase: 14.2.1
current_phase_name: Translate plugin current_phase_name: Translate plugin
status: executing status: verifying
stopped_at: Completed 14.2.1-03-PLAN.md stopped_at: Completed 14.2.1-04-PLAN.md
last_updated: "2026-10-06T11:29:17Z" last_updated: "2026-10-06T12:21:36Z"
last_activity: 2026-10-06 last_activity: 2026-10-06
last_activity_desc: 14.2.1-03 cabana ML fields and proof host boot last_activity_desc: 14.2.1-04 tests, phase gate, and security review
state_head: e3b3475 state_head: ba6e436
progress: progress:
total_phases: 26 total_phases: 26
completed_phases: 12 completed_phases: 12
total_plans: 129 total_plans: 129
completed_plans: 128 completed_plans: 129
milestone_name: milestone milestone_name: milestone
--- ---
@@ -28,12 +28,12 @@ See: .planning/PROJECT.md (updated 2026-09-16)
## Current Position ## Current Position
Phase: 14.2.1 (Translate plugin) — EXECUTING Phase: 14.2.1 (Translate plugin) — VERIFYING
Plan: 3 of 4 complete; next 14.2.1-04 Plan: 4 of 4 complete
Status: Ready for unit/integration tests Status: Ready for phase verification / UAT
Last activity: 2026-10-06 — Cabana markdown/ML fields, docs/dist, and proof host boot Last activity: 2026-10-06 — End-to-end tests, matrices, fail-closed gate, security review
Progress: [████████░░] 75% Progress: [██████████] 100%
## Performance Metrics ## Performance Metrics
@@ -183,6 +183,7 @@ Progress: [████████░░] 75%
| Phase 14.2.1 P01 | 16 min | 3 tasks | 23 files | | Phase 14.2.1 P01 | 16 min | 3 tasks | 23 files |
| Phase 14.2.1 P02 | 15 min | 3 tasks | 21 files | | Phase 14.2.1 P02 | 15 min | 3 tasks | 21 files |
| Phase 14.2.1 P03 | 5 min | 3 tasks | 40 files | | Phase 14.2.1 P03 | 5 min | 3 tasks | 40 files |
| Phase 14.2.1 P04 | 51 min | 3 tasks | 22 files |
## Accumulated Context ## Accumulated Context
@@ -582,6 +583,7 @@ Recent decisions affecting current work:
- [Phase 14.2.1]: MorphName is an import-stable string, never reflect.Type.String(); Translated applies D-11 fallback and TranslatedExact does not. - [Phase 14.2.1]: MorphName is an import-stable string, never reflect.Type.String(); Translated applies D-11 fallback and TranslatedExact does not.
- [Phase 14.2.1]: Locales is HasAdminControllers plus HasNavigation (not HasSettings); Fillable excludes is_default/sort_order; makeDefault uses GORM Updates maps. - [Phase 14.2.1]: Locales is HasAdminControllers plus HasNavigation (not HasSettings); Fillable excludes is_default/sort_order; makeDefault uses GORM Updates maps.
- [Phase 14.2.1]: Task 2 locked grzyby-submodules: proof host is sm-grzybyfunkcjonalne-app with plugins/golem15/user and plugins/golem15/translate; host go.work replace points at ../summercms.go. - [Phase 14.2.1]: Task 2 locked grzyby-submodules: proof host is sm-grzybyfunkcjonalne-app with plugins/golem15/user and plugins/golem15/translate; host go.work replace points at ../summercms.go.
- [Phase 14.2.1]: 14.2.1-04 security review was executor-self-performed (no gsd-security-auditor in this Cursor session); every cited TestName was re-run. Full cabana `-race` needs `-timeout 30m`. WhereIndex falls back to the host column when no index row matches (PHP transWhere).
### Pending Todos ### Pending Todos
@@ -634,6 +636,6 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity ## Session Continuity
Last session: 2026-10-06T11:29:17Z Last session: 2026-10-06T12:21:36Z
Stopped at: Completed 14.2.1-03-PLAN.md Stopped at: Completed 14.2.1-04-PLAN.md
Resume file: None Resume file: None

View File

@@ -0,0 +1,228 @@
---
phase: 14.2.1-translate-plugin
plan: 04
subsystem: i18n
tags: [translate, tests, phase-gate, security-review, testcontainers, race]
requires:
- phase: 14.2.1-03
provides: "cabana ML fields, TranslationWriter adapter, proof-host user+translate boot"
provides:
- "TestTranslateEndToEnd on real Postgres through party/surf/cabana"
- "Migration up/down/re-up, Translator, Translatable, Locales, ML, markdown, SPA matrices"
- "scripts/check-phase14.2.1.sh fail-closed gate ending with Phase 14.2.1 gate passed"
- "14.2.1-SECURITY-REVIEW.md ASVS L1 with T-14.2.1-01..18 and T-14.2.1-SC closed"
affects: [15-journal-plugin]
actuals:
tokens: 38000
tasks: 3
commits: 7
plan_head_before: c7e2e4b4ed1a1048af3dcdaa2c74d37ef70de482
plan_head_after: ba6e436
plugin_repo_head_after: 64f8901
host_repo_head_after: 20108b8
tech-stack:
added: []
patterns:
- "Phase gate uses go -C for sibling plugin and proof host; Docker/Postgres failure is a failure except explicit -short"
- "Security review without a dedicated auditor still re-runs every cited TestName and records that fallback"
key-files:
created:
- ../sm-translate-plugin/integration_test.go
- ../sm-translate-plugin/admin_harness_test.go
- ../sm-translate-plugin/updates/migrations_test.go
- ../sm-translate-plugin/classes/translator_resolve_test.go
- ../sm-translate-plugin/classes/translatable_test.go
- ../sm-translate-plugin/locales_admin_test.go
- modules/cabana/ml_test.go
- modules/cabana/markdown_test.go
- admin/tests/form/MarkdownField.test.ts
- scripts/check-phase14.2.1.sh
- .planning/phases/14.2.1-translate-plugin/14.2.1-SECURITY-REVIEW.md
modified:
- ../sm-grzybyfunkcjonalne-app/boot_test.go
- modules/surf/locale_resolver_test.go
- modules/cabana/openapi_conformance_test.go
- .planning/phases/14.2.1-translate-plugin/14.2.1-VALIDATION.md
key-decisions:
- "WhereIndex follows PHP transWhere: when no golem15_translate_indexes row matches, it falls back to the host column"
- "Full cabana go test -race needs -timeout 30m; the default 10m timed out on the existing suite, not on the new tests"
- "14.2.1-04 security review was executor-self-performed (no gsd-security-auditor in this Cursor session); every cited TestName was re-run"
patterns-established:
- "Fail-closed phase gate: named PASS lines, refuse skip/no-tests/race, sibling go -C, PHP SHA pin, forbidden-surface scan"
- "Integration fixture plugin is process-local via party.Register in tests only and must not appear in plugins.gen.go"
requirements-completed: [D-01, D-02, D-03, D-04, D-05, D-06, D-07, D-08, D-09, D-10, D-11, D-12, D-13, D-14, D-15, D-16, D-17]
coverage:
- id: D1
description: "Real-Postgres e2e activates user+translate+fixture, 403s Locales without manage_locales, saves nested ML en/pl, and proves URL locale precedence"
requirement: D-17
verification:
- kind: e2e
ref: "sm-translate-plugin#TestTranslateEndToEnd"
status: pass
- kind: integration
ref: "sm-grzybyfunkcjonalne-app#TestBootUserTranslate"
status: pass
human_judgment: false
- id: D2
description: "Migration up/down/re-up, Translator D-07 order, Translatable storage/fallback, and Locales authorization/mass-assignment matrices"
requirement: D-07
verification:
- kind: integration
ref: "sm-translate-plugin/updates#TestTranslateMigrationsRollbackAndRemigrate"
status: pass
- kind: unit
ref: "sm-translate-plugin/classes#TestTranslatorResolve"
status: pass
- kind: unit
ref: "sm-translate-plugin/classes#TestTranslatorConcurrentIsolation"
status: pass
- kind: unit
ref: "sm-translate-plugin/classes#TestTranslatableGetSet"
status: pass
- kind: integration
ref: "sm-translate-plugin#TestLocalesAdminForbidden"
status: pass
human_judgment: false
- id: D3
description: "Cabana ML/markdown, surf isolation, SPA fields, OpenAPI FormView types, and fail-closed phase gate"
requirement: D-06
verification:
- kind: unit
ref: "modules/cabana#TestMLNestedSave"
status: pass
- kind: unit
ref: "modules/cabana#TestMarkdownRejectsUnsafeHTML"
status: pass
- kind: unit
ref: "modules/surf#TestLocaleResolver"
status: pass
- kind: automated_ui
ref: "admin/tests/form/MLFields.test.ts"
status: pass
- kind: other
ref: "bash scripts/check-phase14.2.1.sh --all"
status: pass
human_judgment: false
- id: D4
description: "ASVS L1 security review closes T-14.2.1-01 through T-14.2.1-18 and T-14.2.1-SC"
verification:
- kind: other
ref: ".planning/phases/14.2.1-translate-plugin/14.2.1-SECURITY-REVIEW.md"
status: pass
human_judgment: false
- id: D5
description: "Proof-host Locales SPA check as an administrator holding manage_locales"
requirement: D-17
verification: []
human_judgment: true
rationale: "Needs a running host, admin login, and visual confirmation of English then Polski; recorded as end-of-phase UAT"
duration: 51min
completed: 2026-10-06
status: complete
---
# Phase 14.2.1 Plan 04: Translate unit/integration tests and phase gate Summary
**Real-Postgres e2e, decision/security test matrices, fail-closed `check-phase14.2.1.sh`, and an ASVS L1 review that closes T-14.2.1-01..18 plus T-14.2.1-SC**
## Performance
- **Duration:** 51 min
- **Started:** 2026-10-06T11:31:12Z
- **Completed:** 2026-10-06T12:21:36Z
- **Tasks:** 3
- **Files modified:** 22
## Accomplishments
- `TestTranslateEndToEnd` boots user+translate plus a process-local fixture on dedicated Postgres, proves 403 without `golem15.translate.manage_locales`, nested mltext/mlmarkdown save (English on the host, one Polish attribute/index row), WithLocale reads, D-11 fallback, and `/pl/...` winning over cookie + Accept-Language.
- Horizontal matrices cover gormigrate up/down/re-up, Translator D-07 order and `-race` isolation, Translatable storage/fallback/morph isolation, Locales mass-assignment and default-lifecycle guards, cabana ML lift/rollback, unsafe markdown rejection, and SPA markdown composition without `v-html=`.
- `scripts/check-phase14.2.1.sh --all` passed every stage and printed `Phase 14.2.1 gate passed`. PHP pin SHA `725d547ec839f02b5fdc0f0a6faaed601a414d50` is unchanged. High threats are mitigated with re-executed named tests in `14.2.1-SECURITY-REVIEW.md`.
## Task Commits
Each task was committed atomically:
1. **Task 1: End-to-end integration harness**
- `e612b7f` (test) sm-translate-plugin — TestTranslateEndToEnd harness
- `20108b8` (test) sm-grzybyfunkcjonalne-app — expanded TestBootUserTranslate
2. **Task 2: Decision and security matrices**
- `1cfb5ee` (test) sm-translate-plugin — migrate/Translator/Translatable/Locales
- `1bc6200` (test) summercms.go — ML, markdown, resolver, SPA
3. **Task 3: Phase gate, security review, validation sign-off**
- `64f8901` (test) sm-translate-plugin — gofmt harness
- `ba6e436` (test) summercms.go — `scripts/check-phase14.2.1.sh`
**Plan metadata:** (this commit)
Nothing was pushed. The proof host remains local-only.
## Files Created/Modified
- `../sm-translate-plugin/integration_test.go` — production-path e2e
- `../sm-translate-plugin/admin_harness_test.go` — fail-closed TestMain + fixture plugin
- `../sm-translate-plugin/updates/migrations_test.go` — schema, seed, rollback
- `../sm-translate-plugin/classes/translator_resolve_test.go` — D-07 + race isolation
- `../sm-translate-plugin/classes/translatable_test.go` — storage, fallback, morph
- `../sm-translate-plugin/locales_admin_test.go` — 403, mass-assignment, default guards
- `modules/cabana/ml_test.go`, `markdown_test.go` — ML lift/XSS
- `modules/surf/locale_resolver_test.go` — present/absent/isolation
- `admin/tests/form/MarkdownField.test.ts` — SPA sinks
- `scripts/check-phase14.2.1.sh` — fail-closed gate
- `14.2.1-SECURITY-REVIEW.md`, `14.2.1-VALIDATION.md` — review and sign-off
## Decisions Made
- WhereIndex matches PHP transWhere and falls back to the host column when no index row matches.
- Cabana's existing suite needs `-timeout 30m` under `-race`.
- Security review used the Phase 08 fallback (executor-performed) because this session has no `gsd-security-auditor`.
## Deviations from Plan
### Auto-fixed Issues
**1. [Correctness] Polish-index lookup vs host-column fallback**
- **Found during:** Task 1 (e2e)
- **Issue:** Asserting that English slug `hello` must not match a Polish `WhereIndex` failed because `WhereIndex` falls back to the host column when no index row matches.
- **Fix:** Assert the Polish index row is `slug=witaj` and that an unknown slug is `ErrRecordNotFound`.
- **Files modified:** `../sm-translate-plugin/integration_test.go`
- **Verification:** `TestTranslateEndToEnd` PASS
- **Committed in:** `e612b7f`
**2. [Correctness] SPA verify paths**
- **Found during:** Task 2
- **Issue:** `npm --prefix admin test -- --run admin/tests/form/...` finds no files because Vitest include is `tests/**/*.test.ts` under `admin/`.
- **Fix:** Gate and verify use `tests/form/registry.test.ts`, `MLFields.test.ts`, `MarkdownField.test.ts`.
- **Verification:** 53 Vitest tests PASS
- **Committed in:** `ba6e436` (gate)
---
**Total deviations:** 2 auto-fixed (correctness)
**Impact on plan:** Required for PHP-parity lookup and a green SPA stage. No scope creep.
## Issues Encountered
- `go test ./modules/cabana -race` hit the default 10m timeout on the pre-existing suite. New tests pass under `-race` in seconds. The gate uses `-timeout 30m` and the full cabana+surf race passed.
- Plugin `go.mod` gained a sibling `replace` for `sm-user-plugin` and a direct `gocloud.dev v0.46.0` require (already-decided pin) so the e2e harness can activate the real user plugin. Not a new package install (T-14.2.1-SC).
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
Phase 14.2.1 code and gate are green. Ready for `$gsd-verify-work` / milestone audit. Human UAT remains: boot the proof host, open Locales as `manage_locales`, confirm English then Polski. Journal (Phase 15) can implement `Translatable()` on its models.
---
*Phase: 14.2.1-translate-plugin*
*Completed: 2026-10-06*

View File

@@ -0,0 +1,228 @@
---
phase: 14.2.1
slug: translate-plugin
status: verified
threats_total: 19
threats_closed: 19
threats_open: 0
accepted_risks: 0
asvs_level: 1
created: 2026-10-06
verified: 2026-10-06
reviewer: gsd-executor (14.2.1-04 Task 3, self-performed -- see Reviewer Note)
---
# Phase 14.2.1 — Security Review
> Lean Translate plugin (`sm-translate-plugin`), cabana `markdown`/`mltext`/`mlmarkdown`,
> surf `LocaleResolver`, and the proof-host boot of user+translate. Every
> T-14.2.1 threat locked in plans 01–04 is mapped below to executed, named Go
> or Vitest evidence. Unmapped IDs would be a review gap, not an accepted
> risk; none exist.
**Date:** 2026-10-06
**Scope:** Plans 14.2.1-01 through 14.2.1-04; `sm-translate-plugin`;
`summercms.go` modules `cabana` and `surf` plus admin SPA ML/markdown
controls; proof host `sm-grzybyfunkcjonalne-app`.
**Repos grepped:** `sm-translate-plugin`, `summercms.go` (excluding
`.planning/` except this review), `sm-grzybyfunkcjonalne-app`.
## Reviewer Note
14.2.1-04-PLAN.md Task 3 calls for an independent `gsd-security-auditor`
agent pass. This Cursor session has no dedicated security-auditor subagent
(same fallback as Phase 08): the 14.2.1-04 executor performed the review
directly. Every high threat below is closed with source citations and named
tests **re-executed during this review** (2026-10-06), not merely inherited
from earlier plans. The executor checkpoint return states this plainly.
No external API integration: this phase ports a compiled plugin and local
framework/host contracts only.
---
## Verdict Summary
The register contains **19 total threats: 19 closed, 0 open, 0 accepted
risks**. High findings block phase completion; all high rows are mitigate
with executed named tests. PHP pin SHA `725d547ec839f02b5fdc0f0a6faaed601a414d50`
is unchanged.
---
## Trust Boundaries
| Boundary | Description | Data Crossing |
|----------|-------------|----------------|
| untrusted request → Translator | URL prefix, cookies, Accept-Language, principal preferred_locale | locale codes only; must match enabled Locale rows |
| unauthenticated vs `manage_locales` | Locales admin CRUD | JWT + permission grant |
| admin SPA → cabana save | nested mltext/mlmarkdown JSON | locale→string maps lifted before Fill |
| markdown source → HTML | stored body / SPA preview | goldmark without WithUnsafe; SPA interpolates text |
| test fixture → production binary | process-local `acme.fixture` | must not appear in generated plugin imports |
| gate → production claims | skipped containers / stale dist | named PASS + final marker |
---
## Threat Register
| Threat ID | Category | Component | Severity | Disposition | Proof |
|-----------|----------|-----------|----------|-------------|-------|
| T-14.2.1-01 | Tampering | Translator locale inputs | high | mitigate | `classes/translator_resolve_test.go:TestTranslatorResolve` (URL/session/header invalid codes ignored; only enabled `en`/`pl`) |
| T-14.2.1-02 | Information Disclosure | request locale state | high | mitigate | `classes/translator.go` has no process singleton; `TestTranslatorConcurrentIsolation`; `modules/surf/locale_resolver_test.go:TestLocaleResolver` |
| T-14.2.1-03 | Tampering | manual-selection cookie | medium | mitigate | `classes/translator_test.go:TestManualFlagIsNotALocale`; `TestTranslatorResolve/manual_flag_blocks_accept-language` |
| T-14.2.1-04 | Tampering | schema source | high | mitigate | `updates/migrations_test.go:TestTranslateTables`; `TestTranslateMigrationsRollbackAndRemigrate`; no AutoMigrate in production plugin |
| T-14.2.1-05 | Elevation of Privilege | Locales admin | high | mitigate | `locales_admin_test.go:TestLocalesAdminForbidden`; `integration_test.go:TestTranslateEndToEnd` 403 without `golem15.translate.manage_locales` |
| T-14.2.1-06 | Tampering | Locale writes | high | mitigate | `models/locale.go` Fillable excludes `is_default`/`sort_order`; `TestLocalesAdminMassAssignment`; `TestLocalesAdminDefaultGuards` |
| T-14.2.1-07 | Tampering | SetTranslated | high | mitigate | `classes/translatable.go` declared-field + enabled-locale checks; `TestInvalidLocaleRejected`; `TestTranslatableGetSet` |
| T-14.2.1-08 | Information Disclosure | Morph/index queries | medium | mitigate | `TestTranslatableIndexAndMorphIsolation`; MorphName is an import-stable string |
| T-14.2.1-09 | Tampering | ML nested request | high | mitigate | `modules/cabana/field_ml.go` `liftMLValues`; `TestMLNestedSave`; `TestMLFieldTypes` |
| T-14.2.1-10 | Elevation of Privilege | TranslationWriter | high | mitigate | no standalone translate-write route; `TestML/writer_is_not_invoked_before_query_checks`; writer runs after host PK inside the save transaction |
| T-14.2.1-11 | Tampering | mass assignment | high | mitigate | nested non-ML maps still dropped; default scalar only reaches Fill; `TestMLNestedSave` |
| T-14.2.1-12 | Tampering | markdown preview | high | mitigate | `modules/cabana/field_markdown.go:RenderMarkdown`; `TestMarkdownRejectsUnsafeHTML`; `admin/tests/form/MarkdownField.test.ts` |
| T-14.2.1-13 | Tampering | host submodule provenance | medium | mitigate | `TestBootUserTranslate` gitlink `160000`; `scripts/check-phase14.2.1.sh --layout` |
| T-14.2.1-14 | Repudiation | phase gate | high | mitigate | `scripts/check-phase14.2.1.sh` detector refuses skip/no-tests/race; `--self-test` proves fail-closed |
| T-14.2.1-15 | Information Disclosure | concurrent Translator | high | mitigate | `TestTranslatorConcurrentIsolation` under `go test -race`; `TestLocaleResolver/conflicting_requests_stay_isolated` |
| T-14.2.1-16 | Tampering | migration rollback | medium | mitigate | `TestTranslateMigrationsRollbackAndRemigrate` dedicated Postgres |
| T-14.2.1-17 | Tampering | generated admin artifacts | medium | mitigate | `TestMLOpenAPIConformance`; `scripts/check-admin-openapi.sh --check`; `scripts/check-admin-dist.sh` |
| T-14.2.1-18 | Elevation of Privilege | test fixture | high | mitigate | `TestTranslateEndToEndFixtureAbsentFromProduction`; host `plugins.gen.go` has no `acme.fixture` |
| T-14.2.1-SC | Tampering | package installs | high | mitigate | no `go get` / npm install this plan; plugin `replace` of `sm-user-plugin` is a sibling checkout; framework `go.mod`/`admin/package-lock.json` unchanged |
---
## Findings by Threat
### T-14.2.1-01 — locale injection
- **Source:** `sm-translate-plugin/classes/translator.go` `isValid` / `enabledCodes` (enabled Locale rows only).
- **Test evidence (re-run 2026-10-06):** `TestTranslatorResolve` subtests `invalid_url_session_and_header_ignored`, `url_prefix_wins_and_strips`, `accept-language_reduced_to_enabled_codes` — PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-02 — process-wide locale leak
- **Source:** `Translator.Locale` reads `towel.Locale(ctx)` only; no package-level current locale.
- **Test evidence (re-run 2026-10-06):** `TestTranslatorConcurrentIsolation` PASS; `TestLocaleResolver` isolation subtest PASS; plugin `go test ./... -race` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-03 — manual cookie as locale
- **Source:** `hasManualFlag` treats any non-empty value as a flag; `rememberedLocale` reads a different cookie name.
- **Test evidence (re-run 2026-10-06):** `TestManualFlagIsNotALocale` (package classes); `TestTranslatorResolve/manual_flag_blocks_accept-language` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-04 — schema / AutoMigrate
- **Source:** gormigrate files `updates/202610060001`–`005`; plugin production Go has no `AutoMigrate`.
- **Test evidence (re-run 2026-10-06):** `TestTranslateTables`, `TestSeedEnPl`, `TestTranslateMigrationsRollbackAndRemigrate` PASS. Gate `--forbidden` refuses production `AutoMigrate`.
- **Disposition:** closed / mitigate.
### T-14.2.1-05 — manage_locales privilege
- **Source:** `controllers/locales.go` `RequiredPermissions` = `golem15.translate.manage_locales`.
- **Test evidence (re-run 2026-10-06):** `TestLocalesAdminForbidden` PASS; `TestTranslateEndToEnd` 403 on list/schema without the grant PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-06 — Locale mass assignment and default lifecycle
- **Source:** `models/locale.go` `Fillable` is `code,name,is_enabled`; `FormBeforeDelete` / `FormBeforeUpdate` / `makeDefault`.
- **Test evidence (re-run 2026-10-06):** `TestLocalesAdminMassAssignment`, `TestLocalesAdminDefaultGuards` covered by the plugin suite (smoke + full admin tests) PASS in `go test ./...`.
- **Disposition:** closed / mitigate.
### T-14.2.1-07 — undeclared field / invalid locale writes
- **Source:** `classes/translatable.go` `validateCall`.
- **Test evidence (re-run 2026-10-06):** `TestInvalidLocaleRejected`, `TestTranslatableGetSet` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-08 — morph/index isolation
- **Source:** `WhereIndex` predicates on `model_type`, `locale`, `item`, `value`.
- **Test evidence (re-run 2026-10-06):** `TestTranslatableIndexAndMorphIsolation` in the plugin `./...` suite PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-09 — nested ML JSON
- **Source:** `modules/cabana/field_ml.go` `liftMLValues` (enabled locale codes, string values, default required).
- **Test evidence (re-run 2026-10-06):** `TestMLFieldTypes`, `TestMLNestedSave` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-10 — TranslationWriter privilege
- **Source:** `cabana.TranslationWriter` comment and `crud.go` `save`: apply after Create/Update, after permission/query/lock checks.
- **Test evidence (re-run 2026-10-06):** `TestML/writer_is_not_invoked_before_query_checks`, `TestML/writer_failure_rolls_back_host_write` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-11 — ML mass assignment
- **Source:** lift before `ProjectWritableFields`; extra nested keys stay blocked.
- **Test evidence (re-run 2026-10-06):** `TestMLNestedSave` PASS (unlifted nested maps empty; extra locale `de` rejected).
- **Disposition:** closed / mitigate.
### T-14.2.1-12 — stored XSS / markdown
- **Source:** `RenderMarkdown` goldmark without `html.WithUnsafe`; rejects script/iframe/event/javascript/vbscript/data. SPA preview interpolates text, no `v-html=`.
- **Test evidence (re-run 2026-10-06):** `TestMarkdownRejectsUnsafeHTML` (script/iframe/event/javascript/vbscript/data) PASS; Vitest `tests/form/MarkdownField.test.ts` 3/3 PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-13 — submodule provenance
- **Source:** host gitlinks `plugins/golem15/{user,translate}`.
- **Test evidence (re-run 2026-10-06):** `TestBootUserTranslate` PASS; `--layout` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-14 — gate repudiation
- **Source:** `scripts/check-phase14.2.1.sh` JSON detector.
- **Test evidence (re-run 2026-10-06):** `--self-test` PASS (fail/skip/zero/no-tests/race/missing-named).
- **Disposition:** closed / mitigate.
### T-14.2.1-15 — concurrent Translator
- **Source:** context-only locale; Rewrite clones the request.
- **Test evidence (re-run 2026-10-06):** `TestTranslatorConcurrentIsolation` PASS; plugin `-race` PASS; `TestLocaleResolver/conflicting_requests_stay_isolated` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-16 — migration rollback
- **Source:** gormigrate RollbackLast ×5 then remigrate on a dedicated database.
- **Test evidence (re-run 2026-10-06):** `TestTranslateMigrationsRollbackAndRemigrate` PASS.
- **Disposition:** closed / mitigate.
### T-14.2.1-17 — generated artifacts
- **Source:** committed `admin/openapi/admin.json`, `schema.d.ts`, `modules/boardwalk/dist`.
- **Test evidence:** `TestMLOpenAPIConformance` PASS (re-run 2026-10-06); gate `--admin` regenerates and diffs.
- **Disposition:** closed / mitigate.
### T-14.2.1-18 — test fixture in production
- **Source:** `party.Register` of `acme.fixture` is `sync.Once` inside tests only.
- **Test evidence (re-run 2026-10-06):** `TestTranslateEndToEndFixtureAbsentFromProduction` matched by `TestTranslateEndToEnd*` in the plugin `./...` run PASS; host `plugins.gen.go` lists only `golem15.user` and `golem15.translate`.
- **Disposition:** closed / mitigate.
### T-14.2.1-SC — package installs
- **Source:** no new module versions beyond existing stack pins; plugin `replace` of `sm-user-plugin` points at the host submodule checkout; `gocloud.dev v0.46.0` is the already-decided blob pin. Framework `go.mod` / `admin/package-lock.json` were not changed in this phase.
- **Test evidence:** `git log -- go.mod go.sum` on the framework shows no 14.2.1 commits; plugin go.mod Task 1 added a local replace, not a downloaded package.
- **Disposition:** closed / mitigate.
---
## Deferred surfaces confirmed absent
Gate `--forbidden` (re-run 2026-10-06, PASS) refuses production `manage_messages`, `plugin.Open`/`yaegi`, plugin `AutoMigrate`, `acme.fixture` outside tests, Winter/RainLab table names in production Go, consuming-application names in framework docs, and `v-html=` in `admin/src`.
---
## ASVS L1 mapping (selected)
| ASVS | Control | Evidence |
|------|---------|----------|
| V4.1 | Access control on admin | `TestLocalesAdminForbidden` |
| V5.1.3 | Mass assignment | Locale Fillable + ML lift |
| V5.2 | Sanitization / XSS | `RenderMarkdown` + SPA text interpolation |
| V11.1 | Stored locale injection | enabled-row validation |
| V14.2 | Dependency integrity | T-14.2.1-SC |
---
**Approval:** closed — all high threats mitigated with re-executed named tests.

View File

@@ -1,17 +1,17 @@
--- ---
phase: "14.2.1" phase: "14.2.1"
slug: "translate-plugin" slug: "translate-plugin"
# status lifecycle: draft (seeded by plan-phase) → validated (set by validate-phase §6) status: validated
# audit-milestone §5.5 distinguishes NOT-VALIDATED (draft) from PARTIAL (validated + nyquist_compliant: false) (#2117) nyquist_compliant: true
status: draft wave_0_complete: true
nyquist_compliant: false
wave_0_complete: false
created: "2026-10-06" created: "2026-10-06"
validated: "2026-10-06"
--- ---
# Phase 14.2.1 — Validation Strategy # Phase 14.2.1 — Validation Strategy
> Per-phase validation contract for feedback sampling during execution. > Per-phase validation contract. Signed off after plan 14.2.1-04 tests and
> `scripts/check-phase14.2.1.sh` stages were executed.
--- ---
@@ -19,19 +19,19 @@ created: "2026-10-06"
| Property | Value | | Property | Value |
|----------|-------| |----------|-------|
| **Framework** | Go `testing` + testcontainers-go v0.44.0 (Postgres) | | **Framework** | Go `testing` + testcontainers-go v0.44.0 (Postgres) + Vitest for admin SPA |
| **Config file** | none — `go test ./...` | | **Config file** | none — `go test ./...`; gate `scripts/check-phase14.2.1.sh` |
| **Quick run command** | `go test ./... -short -count=1` (plugin + `./modules/cabana` + `./modules/surf`) | | **Quick run command** | `go -C ../sm-translate-plugin test ./... -short -count=1` plus `go test ./modules/cabana ./modules/surf -short -count=1` |
| **Full suite command** | `go test ./... -count=1` in `sm-translate-plugin`, `summercms.go`, and host `go test ./...` | | **Full suite command** | `bash scripts/check-phase14.2.1.sh --all` |
| **Estimated runtime** | ~60 seconds (short); ~180 seconds (full, Postgres) | | **Estimated runtime** | ~60 seconds (short); ~15–25 minutes (full + race, Postgres) |
--- ---
## Sampling Rate ## Sampling Rate
- **After every task commit:** Run `go test ./... -short -count=1` in the repo that changed - **After every task commit:** named verify command in that plan's task
- **After every plan wave:** Run `go test ./... -count=1` and `go vet ./...` in that repo - **After every plan wave:** `go vet` + `go test ./... -count=1` in the repo that changed
- **Before `$gsd-verify-work`:** Full suite must be green in plugin, `summercms.go`, and host - **Before `$gsd-verify-work`:** `bash scripts/check-phase14.2.1.sh --all`
- **Max feedback latency:** 60 seconds (short) - **Max feedback latency:** 60 seconds (short)
--- ---
@@ -40,16 +40,19 @@ created: "2026-10-06"
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | | Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| |---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 14.2.1-W0 | 04 | 0 | D-05 | T-14.2.1-01 | Locales admin requires `golem15.translate.manage_locales` | integration | `go test ./... -run TestLocalesAdminForbidden -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-05 | T-14.2.1-05 | Locales admin requires `golem15.translate.manage_locales` | integration | `go -C ../sm-translate-plugin test . -run TestLocalesAdminForbidden -count=1` | ✅ `locales_admin_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-06 | — | `type: mltext` and `type: mlmarkdown` compile; unknown type fails boot | unit | `go test ./modules/cabana -run TestMLFieldTypes -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-06 | T-14.2.1-09 | `type: mltext` and `type: mlmarkdown` compile; unknown type fails boot | unit | `go test ./modules/cabana -run TestMLFieldTypes -count=1` | ✅ `modules/cabana/ml_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-07 | T-14.2.1-02 | URL prefix wins; invalid code ignored; session/cookie flag; not header-only | unit | `go test ./classes -run TestTranslatorResolve -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-07 | T-14.2.1-01 | URL prefix wins; invalid code ignored; session/cookie flag; not header-only | unit | `go -C ../sm-translate-plugin test ./classes -run TestTranslatorResolve -count=1` | ✅ `classes/translator_resolve_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-08 | — | Seed inserts `en` (default, enabled) and `pl` (enabled, not default); not `de` | integration | `go test ./updates -run TestSeedEnPl -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-08 | — | Seed inserts `en` (default, enabled) and `pl` (enabled, not default); not `de` | integration | `go -C ../sm-translate-plugin test ./updates -run TestSeedEnPl -count=1` | ✅ `updates/migrations_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-09/D-12 | — | `Translatable()` + get/set + `WithLocale` | unit | `go test ./classes -run TestTranslatableGetSet -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-09/D-12 | T-14.2.1-07 | `Translatable()` + get/set + `WithLocale` | unit | `go -C ../sm-translate-plugin test ./classes -run TestTranslatableGetSet -count=1` | ✅ `classes/translatable_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-10 | — | Tables `golem15_translate_locales\|attributes\|indexes\|messages` exist after migrate | integration | `go test ./updates -run TestTranslateTables -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-10 | T-14.2.1-04 | Tables `golem15_translate_locales\|attributes\|indexes\|messages` exist after migrate | integration | `go -C ../sm-translate-plugin test ./updates -run TestTranslateTables -count=1` | ✅ `updates/migrations_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-11 | — | Missing `pl` falls back to default locale column | unit | `go test ./classes -run TestFallbackDefaultLocale -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-11 | — | Missing `pl` falls back to default locale column | unit | `go -C ../sm-translate-plugin test ./classes -run TestFallbackDefaultLocale -count=1` | ✅ `classes/translatable_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | D-17 | — | Host/plugin Activate with user+translate | smoke | `go test ./... -run TestBootUserTranslate -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | D-17 | T-14.2.1-13 | Host/plugin Activate with user+translate | smoke | `go -C ../sm-grzybyfunkcjonalne-app test ./... -run TestBootUserTranslate -count=1` | ✅ `boot_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | T-SEC-01 | T-14.2.1-03 | Unlisted locale never stored | unit | `go test ./classes -run TestInvalidLocaleRejected -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | T-SEC-01 | T-14.2.1-07 | Unlisted locale never stored | unit | `go -C ../sm-translate-plugin test ./classes -run TestInvalidLocaleRejected -count=1` | ✅ `classes/translatable_test.go` | ✅ green |
| 14.2.1-W0 | 04 | 0 | T-SEC-02 | T-14.2.1-04 | Nested ML body not dropped | unit | `go test ./modules/cabana -run TestMLNestedSave -count=1` | ❌ W0 | ⬜ pending | | 14.2.1-W0 | 04 | 0 | T-SEC-02 | T-14.2.1-09 | Nested ML body not dropped | unit | `go test ./modules/cabana -run TestMLNestedSave -count=1` | ✅ `modules/cabana/ml_test.go` | ✅ green |
| 14.2.1-04-1 | 04 | 4 | D-17 | T-14.2.1-18 | Production-path e2e on real Postgres | e2e | `go -C ../sm-translate-plugin test . -run '^(TestTranslateEndToEnd)$' -count=1` | ✅ `integration_test.go` | ✅ green |
| 14.2.1-04-2 | 04 | 4 | D-07 | T-14.2.1-15 | Concurrent request locale isolation | unit | `go -C ../sm-translate-plugin test ./classes -race -run TestTranslatorConcurrentIsolation -count=1` | ✅ `classes/translator_resolve_test.go` | ✅ green |
| 14.2.1-04-3 | 04 | 4 | D-12 | T-14.2.1-12 | Unsafe markdown rejected | unit | `go test ./modules/cabana -run TestMarkdownRejectsUnsafeHTML -count=1` | ✅ `modules/cabana/markdown_test.go` | ✅ green |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
@@ -57,30 +60,30 @@ created: "2026-10-06"
## Wave 0 Requirements ## Wave 0 Requirements
- [ ] `sm-translate-plugin` module and test files listed above - [x] `sm-translate-plugin` module and test files listed above
- [ ] `modules/cabana` ML compile/save tests - [x] `modules/cabana` ML compile/save tests
- [ ] `modules/surf` Resolver-present vs absent locale tests - [x] `modules/surf` Resolver-present vs absent locale tests (`TestLocaleResolver`)
- [ ] Plugin Postgres harness (copy user plugin TestMain / testcontainers) - [x] Plugin Postgres harness (fail-closed TestMain / testcontainers)
- [ ] No new test framework install - [x] No new test framework install
--- ---
## Manual-Only Verifications ## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions | | Behavior | Requirement | Why Manual | Test Instructions | Status |
|----------|-------------|------------|-------------------| |----------|-------------|------------|-------------------|--------|
| Create Gitea remote `git@git.golem15.com:golem15/sm-translate-plugin.git` | D-15 | Requires user credentials on git.golem15.com | User creates public repo; executor verifies clone URL exists | | Gitea remote `git@git.golem15.com:golem15/sm-translate-plugin.git` | D-15 | Requires user credentials | Created in plan 01; clone URL exists | ✅ done (01) |
| Locales admin works in the proof-host SPA | D-17 | Needs running host + admin login | Boot `sm-grzybyfunkcjonalne-app`; open Locales; create/edit locale as permitted user | | Locales admin in the proof-host SPA | D-17 | Needs running host + admin login | Boot the proof host; sign in as an administrator holding `golem15.translate.manage_locales`; open Locales; confirm English then Polski; edit name/enabled; a user without the permission cannot open the controller | ⬜ end-of-phase UAT |
--- ---
## Validation Sign-Off ## Validation Sign-Off
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies - [x] All tasks have `<automated>` verify or Wave 0 dependencies
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify - [x] Sampling continuity: no 3 consecutive tasks without automated verify
- [ ] Wave 0 covers all MISSING references - [x] Wave 0 covers all MISSING references
- [ ] No watch-mode flags - [x] No watch-mode flags
- [ ] Feedback latency < 60s - [x] Feedback latency < 60s (short)
- [ ] `nyquist_compliant: true` set in frontmatter - [x] `nyquist_compliant: true` set in frontmatter
**Approval:** pending **Approval:** validated 2026-10-06 (executor 14.2.1-04). Human UAT for Locales SPA remains end-of-phase.