packagist psalm/plugin-laravel v4.15.4

latest release: v3.15.4
10 hours ago

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, and view() names (#1318). A facade's surface is @method pseudo-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 $with and $withCount (#1321). Supports dotted paths, column selectors, and the $withCount alias 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-escape on closure validation rules (#1352), in inline validate() arrays, in FormRequest rules(), 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 TaintedSql false positives on where() array values for nullable, template-bounded, and intersection builder receivers (#1338, #1350). Values in the map form are PDO-bound, but a receiver typed Builder|null, @template T of Builder, or T&Builder declined the strip.
 /** @param Builder|null $query */
 $query->where(['status' => $request->input('status')]);
-// TaintedSql: Detected tainted SQL
+// clean
  • Stop reporting TaintedFile on uploaded-file extensions (#1324, #1325). getClientOriginalExtension() is the tail after the final dot of a normalized basename and cannot introduce a path segment, and clientExtension() returns a value from Symfony's MIME registry rather than raw client input. All other taint kinds, including include, are unchanged.
 Storage::putFileAs('uploads', $file, Str::ulid() . '.' . $file->getClientOriginalExtension());
-// TaintedFile: Detected tainted file handling
+// clean
  • Suppress cross-class taint flow through the Dispatchable traits (#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 clone stamps 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

Don't miss a new plugin-laravel release

NewReleases is sending notifications on new releases.