--- phase: 14.2.1 slug: translate-plugin status: verified threats_total: 24 threats_closed: 24 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 and 14.2.1-06 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 and 06 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-06; `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, and 14.2.1-06 Task 3 appended CR-01/WR-02 rows the same way. 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 **24 total threats: 24 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-19 | Information Disclosure | hydrateMLRecord / ShowChild | high | mitigate | `modules/cabana/field_ml.go` hydrates only declared ML fields on Show/save and child record payloads; `TestMLHydration`; `TestRelationChildMLNestedSave` | | T-14.2.1-20 | Information Disclosure | FormMeta.EnabledLocales | medium | mitigate | `modules/cabana/http.go` copies locales only on protect()'d form/relation schemas; `admin/tests/form/formState.test.ts` seeds en+pl; `admin/tests/form/MLFields.test.ts` create-empty | | T-14.2.1-21 | Tampering | FormView.adopt | medium | mitigate | `mergeMLValue` applies only to ML fields; `admin/tests/form/formState.test.ts` GET-string merge; `admin/tests/form/MLFields.test.ts` sibling locales | | T-14.2.1-22 | Tampering | fillChild / CreateChild / UpdateChild | high | mitigate | `liftMLValues` before `projectOperation`; `TestRelationChildMLNestedSave` (de 422, no row) | | T-14.2.1-23 | Elevation of Privilege | RelationService.writer | high | mitigate | `http.go` `relations()` Lookup on existing protect()'d child routes; apply after loadParent/loadChild and child PK; `TestRelationChildMLNestedSave` | | 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-19 — ML hydration disclosure - **Source:** `modules/cabana/field_ml.go` `hydrateMLRecord` walks declared `mltext`/`mlmarkdown` fields only; list projection never calls it; `TranslatedExact` skips D-11 so missing pl stays empty. ShowChild uses the same helper after `loadParent`/`loadChild`. - **Test evidence (re-run 2026-10-06):** `TestMLHydration` PASS (english-only pl is `""`); `TestRelationChildMLNestedSave` PASS (create/update/show hydrated maps). - **Disposition:** closed / mitigate. ### T-14.2.1-20 — enabled locale list on schemas - **Source:** `modules/cabana/http.go` `enabledContentLocales` after Lookup, copied onto protect()'d form and relation schema meta; `FormMeta.EnabledLocales` is `json:"enabledLocales,omitempty"` so list Meta omits it. - **Test evidence (re-run 2026-10-06):** `admin/tests/form/formState.test.ts` `seeds every enabled locale as an empty string`; `admin/tests/form/MLFields.test.ts` create-empty selectors list en+pl. - **Disposition:** closed / mitigate. ### T-14.2.1-21 — adopt merge of GET scalars - **Source:** `admin/src/components/form/formState.ts` `mergeMLValue` overlays a host string onto the seed for ML fields only; password fields still clear. - **Test evidence (re-run 2026-10-06):** `admin/tests/form/formState.test.ts` `merges a GET host string onto the seed without dropping sibling locales`; `admin/tests/form/MLFields.test.ts` GET-string cases for mltext and mlmarkdown. - **Disposition:** closed / mitigate. ### T-14.2.1-22 — relation-child nested ML tampering - **Source:** `modules/cabana/relation_child.go` `CreateChild`/`UpdateChild` call `liftMLValues` on the child form before `fillChild`/`projectOperation`; undeclared locale, non-string, missing default, and nil writer with a nested map are 422 before Fill. - **Test evidence (re-run 2026-10-06):** `TestRelationChildMLNestedSave` PASS (create+update persist en/pl; locale `de` is 422 and inserts no row). - **Disposition:** closed / mitigate. ### T-14.2.1-23 — RelationService writer privilege - **Source:** `modules/cabana/http.go` `relations()` Lookup of `TranslationWriter` matches `crud()`; no new HTTP route; `applyMLTranslations` runs after `loadParent`/`loadChild` and after the child row has a primary key. - **Test evidence (re-run 2026-10-06):** `TestRelationChildMLNestedSave` PASS; `recordingWriter.WriteTranslated` refuses a zero PK. - **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.