Hey folks! I've been deep-diving into securing our Laravel applications running in our Kubernetes clusters, and while the default Semgrep rules for PHP are great, I felt they didn't quite capture some of the Laravel-specific patterns that could be problematic. You know how it isβwhen you're managing dozens of microservices with Helm, you want your SAST to be as context-aware as possible.
So, I spent the last couple of weekends building a custom rule pack focused on Laravel. The goal was to catch common security misconfigurations and anti-patterns before they hit our CI/CD pipelines (ArgoCD, of course 😉). I'm really excited to share the core config and get your thoughts!
The pack focuses on a few key areas:
* **Mass Assignment:** Beyond the basic `$fillable` check, looking for hidden risks in `forceFill()` and `update()` patterns.
* **Configuration Sensitivity:** Flagging potential leakage of config values like `app.key` or database credentials into logs or responses.
* **Query Builder Security:** Identifying raw SQL expressions that might be open to injection, even within the Eloquent context.
* **Middleware Misuse:** Ensuring critical security middleware (e.g., `VerifyCsrfToken`) isn't accidentally disabled for specific routes in a risky way.
Here's a snippet of one of the more complex rules I made to detect potentially unsafe mass assignment via `forceFill()` without proper guarding:
```yaml
rules:
- id: laravel-unsafe-forcefill
patterns:
- pattern: (new $MODEL)->forceFill(...)
- pattern-inside: |
public function $FUNC(...) {
...
}
message: "Potential unsafe mass assignment via forceFill(). Ensure the request data is validated or explicitly filtered."
languages: [php]
severity: WARNING
metadata:
category: "security"
technology:
- laravel
```
And here's another one that checks for sensitive configuration values being leaked into application logs, which is a huge no-no for our compliance scans:
```yaml
- id: laravel-sensitive-config-logging
patterns:
- pattern: |
Log::{$LEVEL}(..., config('$CONFIG_KEY'));
- pattern: |
Log::{$LEVEL}(..., Config::get('$CONFIG_KEY'));
message: "Sensitive configuration value '$CONFIG_KEY' is being logged. This could expose secrets."
languages: [php]
severity: ERROR
```
I've packaged these up with about 15 rules so far. The integration into our workflow is slickβI've added the custom pack as a ConfigMap, mounted it into our Semgrep scanner job in the pipeline, and it runs alongside our IaC (Terraform) and Kubernetes manifest scans. The next step I'm pondering is whether to wrap this all up in a Helm chart for easy distribution across teams, or just keep it as a simple git repo referenced in our pipeline definitions.
Has anyone else built custom Semgrep rules for their specific web framework? I'm particularly curious if there are patterns around Laravel's authorization gates or service container usage that I might have missed. Also, how are you all managing and versioning your custom rule packs? I'm leaning towards a GitOps approach, treating the rule pack like any other declarative config.
YAML is not a programming language, but I treat it like one.
Your point about context-aware SAST for Laravel in a microservice environment is critical. While I focus on CRM platforms, this mirrors the exact challenge of writing detection rules for platforms like Salesforce or HubSpot, where out-of-the-box scanners miss framework-specific patterns.
For the middleware checks, I'd suggest adding a rule for the absence of `VerifyCsrfToken` on non-API routes. It's a common oversight during refactoring when a web route group is mistakenly moved under an API prefix, silently removing that protection. Your config leak detection should also scan for calls to `config()` where the first argument is a variable rather than a literal string, as that often indicates dynamic config access that can be risky if the variable source isn't sanitized.
Would you consider extending the query builder rule to flag the use of `DB::raw()` within `orderBy()` clauses? It's a pattern I've seen lead to injection when user input influences the column name or direction.
That's a fair point about dynamic `config()` calls, but I'm more concerned about false positives. What about a legitimate use case like pulling a config value based on the current tenant in a multi-tenant app? The variable source is the tenancy resolver, not user input.
Adding a rule for `DB::raw()` in `orderBy()` seems obvious. But does it actually move the needle on security, or is it just catching code that's already poorly written? I'd be more interested in tracking how often a rule like that catches a genuine, exploitable vuln versus just a style violation.
martech_auditor
You're right to worry about false positives. Dynamic config calls from tenancy middleware are safe. The real issue is when a dev pulls a config key from unsanitized request data, like `$request->input('feature_flag')`. That's the pattern a rule should flag.
Tracking exploit frequency is the whole point. If your SAST is mostly noise, devs start ignoring it. I've seen teams disable entire rule sets after a sprint of false alerts. A rule for `DB::raw()` in `orderBy()` might catch a genuine SQLi once a year, but it'll flag a dozen lazy queries weekly. Is that worth the alert fatigue? Probably not unless you're in a regulated environment.
You need metrics on what your rules actually catch. Otherwise you're just building a linter with extra steps.
Don't panic, have a rollback plan.