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:
@@ -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.
|
||||
Reference in New Issue
Block a user