psalm/plugin-laravel
Psalm plugin for Laravel that adds deep framework-aware static analysis plus taint-based security scanning. Detects SQL injection, XSS, SSRF, shell injection, file traversal, and open redirects by tracking user input flows across functions and services.
[@psalm-taint-source](https://github.com/psalm-taint-source) input for Http\Client\Response methods (#676) @alies-devCollection::sum() return type from mixed to int|float (#680) @alies-devCollection::empty() stub with static<never, never> return type (#679) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.6.1...v4.6.2
Backports type inference and taint analysis improvements from Plugin 4.x to Psalm 6 users.
CookieJar make/queue/forever/forget methods flagged as taint-sink headerStorage::put(), Storage::prepend(), Storage::append() as path/file sinksHttp::get(), Http::post(), Http::send() as SSRF sinkssession() helper and Store methods as taint sources (XSS, SQL injection)View::make(), view() helper, View::share() as HTML sinksMailable subject/to/from as header sinks, body/line/action as HTML sinkseval, evalSha, executeRaw as eval sinksfilename, path, contents, MIME type as taint sourcesencrypt()/decrypt() correctly modeled as taint escape/unescapeheader(), withHeaders(), cookie() as header sinksStubs backported from v4.0–v4.6 to reduce false positives:
count → int<0,max>, get → Collection<int, stdClass>, cursor → LazyCollection), added 20+ method stubs (whereNot, having, from, orderBy, etc.)cursor, pluck, paginators, firstOrCreate; added whereNot, createOrFirst, findSole, chunkMap; @psalm-variadic on with()/without()Stringable/HasBroadcastChannel implements, public increment/decrementBlueprint, ColumnDefinition, ForeignIdColumnDefinition, ForeignKeyDefinition (fluent migration chains)Authenticatable, SessionGuard, TokenGuardfilter() without callback now removes null/false from TValue; flatten(1)/collapse() preserve TValueFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.3.0...v3.4.0
__callStatic) Model calls (#663) @alies-devRelation method calls (#661) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.6.0...v4.6.1
Deep relationship type resolution, Custom query builders, Custom Collections, and smarter validation shapes (thanks to @MDG11).
#[UseEloquentBuilder] attribute and newEloquentBuilder() override (#621) @alies-devSoftDeletes trait methods on custom query builders (#632) @alies-devMethodForwardingHandler for Relation method forwarding (#642) @alies-devmorphTo property type from docblock generic annotations (#652) @alies-dev#[CollectedBy] attribute for custom Eloquent collections (#623) @alies-devCollection::flatten() and collapse() return types to preserve TValue (#619) @alies-devModel::increment()/decrement() as public in stub (#618) @alies-devModelMakeDiscouraged when model has custom make() method (#616) @alies-dev[@psalm-flow](https://github.com/psalm-flow) for Collection get()/first()/pull()/value() default parameter taint propagation (#650) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.5.0...v4.6.0
Three new opt-in rules, expanded taint coverage, and fewer false positives across the board (focus on __() and trans()).
MissingView: Detect missing Blade view files in view() and View::make() calls (#579) @alies-devModelMakeDiscouraged: Detect undefined translation keys in __() and trans() calls (#595) @alies-devMissingTranslation: Warn against Model::make() in favor of new Model() @alies-dev__() and trans() return type to string|array (was mixed) (#592) @alies-dev__() return to string when the translation key is known to exist @alies-devMissingTemplateParam on HasFactory trait (#517) @alies-devModel (#498) @alies-devimplements clauses to 15 stubs (#615) @alies-devmorphTo stub to bypass $this issue in generics @alies-devmorphToMany/morphedByMany signatures @alies-dev[@return](https://github.com/return) static to Stringable stub methods @alies-dev[@psalm-taint-source](https://github.com/psalm-taint-source) input for Route parameter methods (#608) @alies-deveval/executeRaw (Lua injection) @alies-devCookieJar methods @alies-dev$path/$domain sinks to Cookie::expire() and forget() @alies-devStr::of(), str(), and Stringable @alies-devHash::make() and bcrypt() as [@psalm-taint-escape](https://github.com/psalm-taint-escape) system_secret @alies-devTested against 10 real-world Laravel apps (bagisto, coolify, monica, pixelfed, solidtime, unit3d, vito, and others). Combined results vs v4.4.0:
| Metric | v4.4.0 | v4.5.0 | Delta |
|---|---|---|---|
| Total issues | 84,503 | 76,123 | -9.9% |
| Plugin-caused false positives | 5,115 | 4,155 | -18.8% |
| Security findings (taint) | 83 | 84 | +1 |
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.4.0...v4.5.0
Collection, Model, Builder stubs (backport them from 4.x) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.2.2...v3.3.0
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.2.1...v3.2.2
MorphTo relationships @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.2.0...v3.2.1
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v3.2.0
This is the biggest release since v4.0.
Is release is focused on Validator and FormRequest classes and provides best-in-class type infer for them.
FormRequest (#577) @alies-devView\Factory and View\View methods (#580) @alies-devJs::from() and Js::encode() (#573) @alies-devSession\Store::get() and other) (#557) @alies-devMail and Notification classes (#556) @alies-devRequest::integer() and Request::float() (#575) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.3.2...v4.4.0
| Dependency | plugin v3 | plugin v4 |
|---|---|---|
| PHP | ^8.2 | ^8.2 |
| Laravel | 11, 12 | 12, 13 |
| Psalm | 6, 7 (beta) | 7 only |
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.3.1...v4.3.2
Attribute::make() (#552)[@psalm-taint-escape](https://github.com/psalm-taint-escape) html for e() helper to avoid false negatives (#551)stubs/taintAnalysis/ into stubs/common/ (#553)Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.3.0...v4.3.1
This release focuses on migration schema analysis for better Eloquent attribute type inference.
Schema call patterns (connection chaining, class constants, custom facades) (#526)foreignIdFor() column type from referenced model's primary key (#523)Blueprint::datetimes() and fix ulid() default column name (#531)mixed type for unknown Blueprint methods (custom DB types added by macros) (#528)Collection::map() return type, add Builder::select() and ResponseTrait::cookie() stubs (#548)[@psalm-taint-escape](https://github.com/psalm-taint-escape) sql for Connection::escape() (#547)UploadedFile and encrypt/decrypt helpers (#546)Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.2.0...v4.3.0
Relationship accessors without generics — The plugin now resolves Eloquent relationship property types even when methods lack generic annotations. Previously, $user->posts required [@return](https://github.com/return) HasMany<Post, User> to get a precise type. Now the plugin parses the method body AST to extract the related model from $this->hasMany(Post::class), falling back gracefully to bounded types.
Static Query Builder methods on Models — User::where(...), User::orderBy(...), and model scopes now resolve with the correct Builder<User> return type, enabling full type inference through query chains starting from the model class.
SQL schema dump support — The plugin now parses php artisan schema:dump output (MySQL, PostgreSQL, SQLite) as a base layer for model attribute discovery. PHP migrations are applied on top, matching Laravel's own resolution order.
🛡️ Security: new taint sinks — Added XSS detection through HtmlString (which bypasses Blade escaping) and path traversal detection through Storage facade methods (put, writeStream, delete, copy, move, etc.).
Query\Builder methods and scopes on Model classes (#508)Schema\ColumnDefinition, ForeignIdColumnDefinition, and ForeignKeyDefinition fluent methods (#501)HtmlString to detect XSS bypass of Blade escaping (#491)Storage facade / FilesystemAdapter path traversal detection (#492)up() (#509)nullableTimestampsTz() switch case in schema aggregatorcount/update/increment/decrement return type to int<0, max> (#499)hasUserPseudoProperty() helper to reduce redundant storage lookups$codebase->progress->debug() to relationship resolution catch blocks for --debug traceabilityfindStubFiles() — errors now propagate to the top-level handlerFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.1.0...v4.2.0
pluck() value type from model [@property](https://github.com/property) annotations (#488) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.0.1...v4.1.0
Collection::filter() return type when called without callback (#467) @alies-devroute helper function stub as not needed anymore @alies-devonce helper function stub as not needed anymore @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.0.0...v4.0.1
The biggest release since the plugin was created. 90% of the codebase was rewritten for stability, performance, and deeper Laravel coverage.
./vendor/bin/psalmAttribute<TGet, TSet> templates all work. Use both tools together: Larastan for types, psalm-plugin-laravel for securitybarryvdh/laravel-ide-helper dependency -- facades and model properties are now resolved natively by the pluginInvalidConsoleArgumentName,
InvalidConsoleOptionName,
NoEnvOutsideConfig[@property](https://github.com/property) declarations take precedence over migration-discovered propertiescasts() parsing without method executionscopeXxx() methods and Laravel 12+ #[Scope] attribute, plus the Scope interfaceafter() closures, Blueprint::rename(), addColumn(), vector columns, and auto-discovery of directories registered via loadMigrationsFrom()| Dependency | v3 | v4 |
|---|---|---|
| PHP | ^8.2 | ^8.3 |
| Laravel | 11, 12 | 12, 13 |
| Psalm | 6, 7 (beta) | 7 only |
Eloquent relation generics now require a declaring model parameter (e.g., BelongsTo<Foo> becomes BelongsTo<Foo, self>).
composer require --dev psalm/plugin-laravel:^4.0 -W
psalm-plugin-laravel is the only free tool that combines Laravel-aware type analysis with dataflow-based taint vulnerability detection:
| Vulnerability | Laravel surface | OWASP |
|---|---|---|
| SQL Injection | DB::statement(), DB::unprepared(), query builder raw methods |
A03:2021 |
| Shell Injection | Process::run(), Process::pipe() |
A03:2021 |
| File Traversal | Storage::get(), Storage::put(), 15 Filesystem methods |
A01:2021 |
| SSRF | Http::get(), Http::post(), 6 HTTP client methods |
A10:2021 |
| XSS | Response::setContent(), ResponseFactory::make() |
A03:2021 |
| Open Redirect | Redirect::to(), Redirect::away() |
A10:2021 |
| Crypto tracking | Encrypter, HashManager taint-escape/unescape |
A02:2021 |
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v4.0.0
See v4.0.0 Beta 1 release for full list of major changes
composer require --dev psalm/plugin-laravel:^4.0@beta -W
If you have "minimum-stability": "stable", and got Your requirements could not be resolved to an installable set of packages.: error
composer config minimum-stability beta
composer config prefer-stable true
composer require --dev vimeo/psalm:^7.0@beta psalm/plugin-laravel:^4.0@RC -W
See Upgrading from v3 to v4 for details.
RC Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.0.0-rc.1...v4.0.0-rc.2
Major Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v4.0.0-rc.2
See v4.0.0 Beta 1 release for full list of major changes
composer require --dev psalm/plugin-laravel:^4.0@beta -W
If you have "minimum-stability": "stable", and got Your requirements could not be resolved to an installable set of packages.: error
composer config minimum-stability beta
composer config prefer-stable true
composer require --dev vimeo/psalm:^7.0@beta psalm/plugin-laravel:^4.0@RC -W
See Upgrading from v3 to v4 for details.
NoEnvOutsideConfig rule to detect env() calls outside config directory https://github.com/psalm/psalm-plugin-laravel/pull/456CastsMethodParser AST traversal — getAttribute('parent') is always null https://github.com/psalm/psalm-plugin-laravel/pull/458createMany param types and CastsMethodParser AST parent lookup https://github.com/psalm/psalm-plugin-laravel/pull/462RC/Beta Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.0.0-beta.2...v4.0.0-rc.1
Major Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v4.0.0-rc.1
Beta Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v4.0.0-beta.1...v4.0.0-beta.2
Major Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v4.0.0-beta.2
[@property](https://github.com/property) declarations (take precedence over migration-discovered properties)scopeXxx() and #[Scope] attribute)barryvdh/laravel-ide-helper dependency — facades and model properties are now resolved nativelycomposer require --dev psalm/plugin-laravel:^4.0@beta -W
Major Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.5...v4.0.0-beta.1
dropColumn() with array argument in SchemaAggregator (#448) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.4...v3.1.5
retry() helper @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.3...v3.1.4
unsignedBigInteger, increments, foreignId, id, and the ->unsigned() modifier are now recognized, enabling non-negative-int inference for unsigned columns @alies-dev->default() calls in migrations are now parsed and tracked, enabling more accurate type inference for model attributes with defaults @alies-devif blocks) inside migration closures no longer cause subsequent column definitions to be skipped @alies-devforeignIdFor() column name: foreignIdFor(User::class) now correctly resolves to user_id instead of id @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.2...v3.1.3
barryvdh/laravel-ide-helper dependency @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.1...v3.1.2
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.1.0...v3.1.1
Taint Analysis: Security Analysis in Psalm. Example 1, Example 2
retry() helper (#417) @alies-devFull Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.0.5...v3.1.0
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.0.4...v3.0.5
dispatch() match upstream Laravel (#407) @saulens22Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v3.0.3...v3.0.4
dev to composer keywords (#397) @alies-devInternal changes:
Full Changelog: https://github.com/psalm/psalm-plugin-laravel/compare/v2.12.1...v2.12.2
How can I help you explore Laravel packages today?