Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions doc/ui/jsvar.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ write, and connection behavior.

`ClientCheck` is required for browser writes; nil denies them. It receives the
complete tentative Go value under the store's write lock. Validate authorization
through the originating Element and validate the full value, including parent and
root changes. An error or panic rolls the proposal back. The callback must only
inspect: do not mutate or retain tentative data, reacquire the lock, or call a
store setter. Every binding receives the same JSON value; use separate stores
for data with different visibility.
through the originating Element and validate the full value, including fields
omitted from JSON and changes through parent or root paths. A `null` proposal for
a struct zeroes all its fields, including unexported and `json:"-"` fields. An
error or panic rolls the proposal back. The callback must only inspect: do not
mutate or retain tentative data, reacquire the lock, or call a store setter.

Every binding receives the same JSON value; use separate stores for data with
different visibility. Proposal outcomes can depend on Go fields omitted from
JSON, even when the JSON values are identical. Keep secrets outside the bound
value.

Browser JSON numbers are converted to the destination Go type before validation.
Conversion can truncate or wrap values: `2.9` becomes `2` for `int`, and `300`
Expand Down
12 changes: 7 additions & 5 deletions lib/ui/jsvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ func validateJsVarPath(path string) error {
// JsVarCheck validates the complete tentative state of a browser proposal.
//
// A nil check denies browser writes. The check runs under the store's write lock
// after jq tentatively applies a changed value. It must only inspect next;
// after jq tentatively applies a changed Go value. It must only inspect next;
// an error or panic rolls the proposal back. The source can authorize a user
// or session. Validate the complete value, including changes through parent
// and root paths. The check sees converted Go values, not raw browser JSON;
// see [JsVarBinding.JawsInput].
// or session. Validate the complete value, including fields omitted from JSON
// and changes through parent or root paths. A null proposal for a struct zeroes
// all its fields, including unexported and json:"-" fields. The check sees
// converted Go values, not raw browser JSON; see [JsVarBinding.JawsInput].
type JsVarCheck[T any] func(source *jaws.Element, next *T, path string) error

// JSONSizeCheck limits the encoded size of a tentative JsVar store value.
Expand Down Expand Up @@ -117,7 +118,8 @@ func JSONSizeCheck[T any](maxBytes int) (check JsVarCheck[T]) {
// ClientCheck and ExtraTags must be configured before first use and remain
// unchanged while the store is active.
// Every binding sees the same JSON value; ClientCheck controls writes, not
// disclosure.
// disclosure. Proposal outcomes can depend on Go fields omitted from JSON,
// even when the JSON values are identical. Keep secrets outside the bound value.
//
// All reads and writes of the bound value must use the supplied locker. Server
// mutations must use SetPath, DeletePath, or WriteLocked to publish changes.
Expand Down
Loading