fix(10.1): WR-03 walk the whole partial view model before rendering
refusedViewModel compared only the top-level type with the controller's model. It now walks the type through pointers, slices, arrays, maps, struct fields and the results of exported methods, and the values held in interface-typed members, refusing the controller's model, any other GORM model (TableName, a gorm tag, gorm.Model, gorm.DeletedAt) and html/template's trusted content types anywhere in that structure.
This commit is contained in:
@@ -53,7 +53,7 @@ Every path under the prefix that no API route matches is served by the admin SPA
|
||||
|
||||
A partial is an `html/template` file next to the controller's YAML: `headerPartial: stats` and `path: stats` both resolve to `{ConfigDir}/_stats.htm`; Winter's `$/` and `~/` paths are not supported. The template's root is `.Data`, the value the controller's `PartialData(ctx, name, record)` returns, and `trans "<key>"` translates a phrase key in the request locale. `record` is nil for a header partial and for a form partial on the create form; with `?id=` it is the record cabana loaded through the controller's `pact.FormExtendQuery` scope, so a plugin never looks a record up by a request id itself.
|
||||
|
||||
The view model must be a curated struct built for the template. cabana refuses a value of the controller's own model type (or a pointer to or a collection of it) and a type that carries `html/template`'s pre-escaped content types, so escaping stays on for every record value. The rendered output is parsed with `golang.org/x/net/html` and walked through an allowlist:
|
||||
The view model must be a curated struct built for the template. cabana walks its type through pointers, slices, arrays, maps, struct fields and the results of its exported methods (templates call methods), and the values held in interface-typed members such as `map[string]any`. It refuses the controller's own model type, any other GORM model (a struct with a `TableName` method, a `gorm` struct tag, `gorm.Model` or `gorm.DeletedAt`) and `html/template`'s pre-escaped content types anywhere in that structure, so escaping stays on for every record value. A method that returns an interface is not called, so its run-time result is not checked. The rendered output is parsed with `golang.org/x/net/html` and walked through an allowlist:
|
||||
|
||||
- Elements: `div span p strong em b i u s small mark code pre br hr ul ol li dl dt dd h2 h3 h4 h5 h6 table thead tbody tfoot tr th td caption section header footer figure figcaption blockquote q abbr time data meter progress sup sub a img`. Any other element is unwrapped (its children stay); `script style template iframe object embed noscript textarea title xmp svg math form input button select link meta base` are removed with everything inside them, and comments disappear.
|
||||
- Attributes: `class title lang dir role`, `aria-*` and `data-*` everywhere; `a[href]` and `img[src]` only for a same-origin path starting with exactly one `/` (links may also use `#fragment`); `img[alt width height]`, `td`/`th[colspan rowspan scope]`, `time[datetime]`, `data[value]`, `meter[value min max low high optimum]`, `progress[value max]`. `id`, `style` and every event handler are dropped.
|
||||
|
||||
Reference in New Issue
Block a user