Add 4 Laravel security rules: raw SQL, open redirect, mass assignment, weak hashing#3808
Add 4 Laravel security rules: raw SQL, open redirect, mass assignment, weak hashing#3808momenbasel wants to merge 2 commits intosemgrep:developfrom
Conversation
…ssignment, and weak hashing - laravel-raw-db-statement: Detects DB::statement/DB::raw/DB::select with string concatenation - laravel-unvalidated-redirect: Detects redirect() with unvalidated user input - laravel-mass-assignment-fillable-star: Detects $fillable = ['*'] wildcard - laravel-weak-password-hash: Detects md5/sha1/crypt for password hashing instead of Hash::make
…bool The taint rule was flagging type-cast variables as vulnerable. PHP type casts like (int), (float), and (bool) effectively sanitize user input for SQL injection since the result is always a safe scalar type.
|
|
There was a problem hiding this comment.
Pull request overview
Adds four new Semgrep security rules to the existing Laravel/PHP ruleset, each with a dedicated .php test fixture using ruleid: / ok: annotations to validate matching behavior.
Changes:
- Add a taint-mode rule to detect user-controlled input flowing into raw
DB::statement/select/insert/update/delete/rawSQL usage. - Add a rule to detect potential open redirects via
redirect()/Redirect::to()when fed from request input or superglobals. - Add rules to detect risky Eloquent configuration (
$fillable = ['*']) and weak/nonstandard password hashing patterns (md5/sha1/crypt).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| php/laravel/security/laravel-raw-db-statement.yaml | New taint rule for raw DB facade SQL sinks. |
| php/laravel/security/laravel-raw-db-statement.php | Test cases for raw DB facade SQL sinks. |
| php/laravel/security/laravel-unvalidated-redirect.yaml | New pattern rule for request-driven redirects (open redirect). |
| php/laravel/security/laravel-unvalidated-redirect.php | Test cases for request-driven redirects. |
| php/laravel/security/laravel-mass-assignment-fillable-star.yaml | New pattern rule for $fillable = ['*'] in Eloquent models. |
| php/laravel/security/laravel-mass-assignment-fillable-star.php | Test cases for $fillable = ['*'] and safe variants. |
| php/laravel/security/laravel-weak-password-hash.yaml | New pattern rule for weak/nonstandard password hashing usage. |
| php/laravel/security/laravel-weak-password-hash.php | Test cases for weak hashing patterns and safe Laravel hashing APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pattern-sanitizers: | ||
| - patterns: | ||
| - pattern-either: | ||
| - pattern: intval(...) | ||
| - pattern: (int) $X | ||
| - pattern: (float) $X |
There was a problem hiding this comment.
pattern-sanitizers treats htmlspecialchars(...) as a SQL-injection sanitizer, but it does not prevent SQL injection and will create false negatives. Also, the tests include a safe (int) cast (safe_cast in the paired .php file) but this rule doesn’t list casts as sanitizers, so the ok: case may still be reported. Remove htmlspecialchars(...) from sanitizers and add integer-cast sanitizers (e.g., (int) ..., (integer) ..., and/or intval(...) only).
| class AnotherSafeModel extends Model | ||
| { | ||
| /** | ||
| * Using guarded to block specific fields is also acceptable. | ||
| */ | ||
| // ok: laravel-mass-assignment-fillable-star | ||
| protected $fillable = ['title', 'body', 'slug']; |
There was a problem hiding this comment.
The docblock says this model is safe because it uses guarded, but the code actually uses $fillable. This is misleading in a rule test file (it documents expected safe patterns) and should be corrected to match the implementation (or change the example to actually use $guarded).
| owasp: | ||
| - A01:2021 - Broken Access Control | ||
| - A04:2025 - Broken Access Control | ||
| technology: |
There was a problem hiding this comment.
OWASP metadata is inconsistent with the rest of the Laravel security rules: other rules label “Broken Access Control” as A01:2025, but this rule uses A04:2025. Please align this to the numbering used elsewhere in php/laravel/security/*.yaml for the same OWASP category to keep metadata consistent.
| message: >- | ||
| A weak hashing function (`md5`, `sha1`, or `crypt`) is used on a variable whose | ||
| name suggests it contains a password. These algorithms are not designed for password | ||
| hashing and are vulnerable to brute-force and rainbow table attacks. | ||
| Use Laravel's `Hash::make()` or the `bcrypt()` helper instead, which use bcrypt | ||
| with a secure cost factor by default. |
There was a problem hiding this comment.
The message groups crypt() with inherently weak hashes (md5/sha1) and states it is “not designed for password hashing”. In PHP, crypt() can be configured to use strong schemes (e.g., bcrypt with $2y$...), so flagging all crypt($password, ...) usages as “weak” is misleading and likely to cause noisy findings. Consider either removing crypt() from this rule, narrowing it to clearly weak crypt usages, or adjusting the rule id/message to focus on “non-Laravel password hashing API” rather than “weak hashing”.
Summary
Adds 4 new security rules for Laravel/PHP applications with corresponding test files:
laravel-raw-db-statementDB::statement/select/insert/update/delete/rawlaravel-unvalidated-redirectredirect()with unvalidated$request->input()laravel-mass-assignment-fillable-star$fillable = ['*']wildcard bypassing mass assignment protectionlaravel-weak-password-hashmd5()/sha1()/crypt()for passwords instead ofHash::make()Each rule includes a
.phptest file withruleid:andok:annotations.Context
The existing Laravel security ruleset has 11 rules. These additions cover critical gaps:
laravel-sql-injection(which coverswhereRaw/orderByRaw) by detectingDB::facade methods (statement,select,insert,update,delete,raw) with tainted input. Includes sanitizers for type casting (intval,(int)).laravel-dangerous-model-construction(which catches$guarded = []) by detecting the$fillable = ['*']wildcard variant.Test Plan
semgrep --test php/laravel/security/to validate all rule/test pairs passruleid:annotation triggers and eachok:annotation does not