Extends taint reporting to the call forms Laravel applications actually write — facade statics, response() on its contract, and view() names — and clears the false positives that surfaced alongside it.
Features
- 🛡️ Report taint sinks on facade static calls,
response()contract methods, andview()names (#1318). A facade's surface is@methodpseudo-methods resolved through__callStatic, whose parameters have no docblock to carry a sink, so the static form was silent while the chained form fired.
Redirect::to($request->input('next'));
-// silent: the facade pseudo-parameter carried no sink
+// TaintedHeader: Detected tainted header- Detect undefined relations in model eager-load defaults
$withand$withCount(#1321). Supports dotted paths, column selectors, and the$withCountalias grammar; defers when an intermediate related model cannot be resolved.
class Post extends Model
{
protected $with = ['auther'];
- // silent: eager-load defaults were never validated
+ // UndefinedModelRelation: relation 'auther' is not defined on Post
}Fixes
- Honour
@psalm-taint-escapeon closure validation rules (#1352), in inlinevalidate()arrays, in FormRequestrules(), and when the closure is the field's whole rule. Previously the only way to assert a rule made a value safe was to extract it into a dedicated Rule class.
$request->validate([
'path' => ['required', /** @psalm-taint-escape file */ static fn ($attr, $value, $fail) => /* ... */],
]);
-// TaintedFile: a closure body is opaque, so no rule could assert safety
+// clean- Fix
TaintedSqlfalse positives onwhere()array values for nullable, template-bounded, and intersection builder receivers (#1338, #1350). Values in the map form are PDO-bound, but a receiver typedBuilder|null,@template T of Builder, orT&Builderdeclined the strip.
/** @param Builder|null $query */
$query->where(['status' => $request->input('status')]);
-// TaintedSql: Detected tainted SQL
+// clean- Stop reporting
TaintedFileon uploaded-file extensions (#1324, #1325).getClientOriginalExtension()is the tail after the final dot of a normalized basename and cannot introduce a path segment, andclientExtension()returns a value from Symfony's MIME registry rather than raw client input. All other taint kinds, includinginclude, are unchanged.
Storage::putFileAs('uploads', $file, Str::ulid() . '.' . $file->getClientOriginalExtension());
-// TaintedFile: Detected tainted file handling
+// clean-
Suppress cross-class taint flow through the
Dispatchabletraits (#1334). Psalm conflated taint nodes from the shared trait bodies, so an argument dispatched to one job appeared to reach an unrelated job's constructor sink. Genuine Bus and Event taint is still reported. -
Fingerprint the migration schema cache on file contents instead of modification times (#1346). A
git clonestamps every file with the checkout time, so the fingerprint changed on every CI run and the cache never hit even when the restored schema was still valid.
Full Changelog: v4.15.3...v4.15.4