Skip to content

v3.35.4 (security)

Choose a tag to compare

@tjloong tjloong released this 13 Oct 06:52
· 11 commits to main since this release
f3e5840

Security

The filter() query macro could put part of a client-chosen filter key into raw SQL on a date column (MySQL). Any Livewire component that passes an unlocked public $filters array to ->filter() (the documented table pattern) was affected. Upgrade promptly.

This release also stops filter() from being used to probe columns a model hides: keys on a column in $hidden, or outside a non-empty $visible, are now ignored, and operators are limited to a fixed list.

filter() still lets a client filter any other visible column and call any named scope with the value it sends. Where that matters, pass only the keys you allow: filter(Arr::only($this->filters, ['search', 'status', 'dates'])). Do not mark a $filters that atom's table controls bind to (wire:model="filters.…") as #[Locked]: Livewire then refuses every update from them and search, filters and chips stop working. (Corrected after publication: an earlier version of this note recommended #[Locked].)

Behaviour changes (for release notes)

  • Filter keys are restricted to ^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z0-9_]+)*$. A digit-leading or hyphenated column name, and a JSON path segment with - or [0], are now skipped instead of filtered.
  • A key with more than one : (a:b:c) is skipped; before, the middle part was ignored.
  • A sub-path on a non-JSON column is skipped.
  • A column in the model's $hidden, or outside a non-empty $visible, can no longer be filtered through filter(). $visible is a serialisation list, so a host whose $visible omits created_at, status or foreign-key columns will have filters on them silently dropped. A host that filters on such a column on purpose must do it outside filter().
  • col:<operator> accepts only = != <> < <= > >= like and not like. Others (regexp, rlike, is, bitwise...) are a no-op; before, Laravel either applied them or demoted them to = and bound the operator text as the value. Operators are now case-insensitive.
  • Array value on a date column, on like, nested arrays, or an array for the search scope: skipped; before, some of these threw.
  • A list on a plain column: = or no operator is whereIn (unchanged), != / <> is now whereNotIn (it used to be whereIn, i.e. inverted), <, <=, >, >= are skipped. On a JSON column a list means "contains all of" (whereJsonContains) for =/no operator and "does not contain all of" (whereJsonDoesntContain) for != / <>; ordering operators are skipped.
  • A date object (Carbon, DateTimeImmutable) is passed on as an object, so Laravel formats and binds it as usual: whereDate() on a date column, a normal binding on datetime and plain columns, and as sent to a scope. like with a date object is skipped on a plain column.
  • not like now gets the same %value% wrapping as like.

Refs #86. (#96)