psalm/plugin-laravel
Laravel Psalm plugin for deep static analysis plus taint-based security scanning. Detects SQL injection, XSS, SSRF, shell injection, path traversal, and open redirects by tracking user input through Laravel code without running it.
A public legacy Eloquent attribute accessor or mutator, getXxxAttribute() / setXxxAttribute(). Laravel's convention is protected. Enabled by default; see How to disable.
It is dispatched indirectly through __get() / __set() magic and never called by its declared name, so public only widens the model's API surface. It breaks nothing on the path anyone writes, so it is a pure convention nit on otherwise-correct code.
Reported as an error only at Psalm's strictest level (1) and downgraded for everyone else, so a hard failure is effectively opt-in through maximum strictness. A method whose visibility is forced by a contract (an interface method, a parent override, or an abstract trait method) is not reported, since it cannot be narrowed.
class Post extends Model
{
protected function getTitleAttribute(): string // public here would be reported
{
return ucfirst($this->attributes['title']);
}
}
New code should prefer the modern accessor form, which is out of scope for this check:
protected function title(): Attribute
{
return Attribute::get(fn (mixed $value, array $attributes): string => ucfirst($attributes['title']));
}
Change public to protected, or migrate an accessor to the modern Attribute form above. Call sites are unaffected.
<issueHandlers>
<PluginIssue name="PublicModelAccessor" errorLevel="suppress" />
</issueHandlers>
Only the legacy accessor / mutator forms are detected: getXxxAttribute(), setXxxAttribute(). The modern Attribute-returning accessor form is out of scope, the framework's own getAttribute() / setAttribute() are never matched, and private is left alone. Legacy scopeXxx() query scopes are deliberately not reported, since public is Laravel's documented idiom for them.
How can I help you explore Laravel packages today?