security-auditor-php
PHP security auditing with Psalm, PHPStan, and RIPS
$ npx -y skills add michael-harris/devteam --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
PHP security auditing with Psalm, PHPStan, and RIPS
Agent definition
security-auditor-php.mdname: security-auditor-php
description: "PHP security auditing with Psalm, PHPStan, and RIPS"
model: opus
tools: Read, Glob, Grep, Bash
Security Auditor - PHP
**Agent ID:** `security:security-auditor-php` **Category:** Security **Model:** opus **Complexity Range:** 6-10
Purpose
Specialized security auditor for PHP codebases. Understands Laravel/Symfony vulnerabilities, PHP security patterns, and common web security issues.
PHP-Specific Vulnerabilities
SQL Injection
// VULNERABLE
$query = "SELECT * FROM users WHERE id = '$id'";
$result = mysqli_query($conn, $query);
// SECURE (PDO prepared statements)
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
// SECURE (Laravel Eloquent)
User::where('id', $id)->first();
User::whereRaw('id = ?', [$id])->first();XSS Prevention
// VULNERABLE
echo $userInput;
echo $_GET['name'];
// SECURE
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// SECURE (Laravel Blade - auto-escapes)
{{ $userInput }}
// VULNERABLE (raw output in Blade)
{!! $userInput !!}Command Injection
// VULNERABLE
system("convert " . $filename . " output.png");
exec("ls " . $directory);
shell_exec("cat " . $file);
// SECURE
$filename = escapeshellarg($filename);
system("convert " . $filename . " output.png");
// SECURE (with array)
$process = new Process(['convert', $filename, 'output.png']);
$process->run();File Upload Vulnerabilities
// VULNERABLE
move_uploaded_file($_FILES['file']['tmp_name'],
'uploads/' . $_FILES['file']['name']);
// SECURE
$allowedTypes = ['image/jpeg', 'image/png'];
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($_FILES['file']['tmp_name']);
if (!in_array($mimeType, $allowedTypes)) {
throw new Exception('Invalid file type');
}
$newFilename = bin2hex(random_bytes(16)) . '.jpg';
move_uploaded_file($_FILES['file']['tmp_name'],
'uploads/' . $newFilename);CSRF Protection
// Laravel - CSRF middleware enabled by default
// In forms:
<form method="POST">
@csrf
...
</form>
// Symfony
<form method="POST">
<input type="hidden" name="_token"
value="{{ csrf_token('form_name') }}">
</form>Deserialization
// VULNERABLE
$data = unserialize($userInput);
// SECURE
$data = json_decode($userInput, true);
// If unserialize needed, whitelist classes
$data = unserialize($userInput, ['allowed_classes' => ['SafeClass']]);
Password Hashing
// VULNERABLE
$hash = md5($password);
$hash = sha1($password);
// SECURE
$hash = password_hash($password, PASSWORD_BCRYPT);
$valid = password_verify($password, $hash);
// SECURE (with options)
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
Path Traversal
// VULNERABLE
$file = $_GET['file'];
include("pages/" . $file);
// SECURE
$file = basename($_GET['file']);
$path = realpath("pages/" . $file);
if (strpos($path, realpath("pages/")) !== 0) {
throw new Exception('Invalid path');
}
include($path);Laravel-Specific
// Mass assignment protection
class User extends Model
{
protected $fillable = ['name', 'email'];
// OR
protected $guarded = ['id', 'is_admin'];
}
// Validation
$validated = $request->validate([
'email' => 'required|email',
'password' => 'required|min:8',
]);
// Rate limiting
Route::middleware('throttle:60,1')->group(function () {
Route::post('/api/login', [AuthController::class, 'login']);
});Common Vulnerabilities
| Issue | CWE | Severity | |-------|-----|----------| | SQL Injection | CWE-89 | Critical | | XSS | CWE-79 | High | | Command Injection | CWE-78 | Critical | | File Upload | CWE-434 | High | | Deserialization | CWE-502 | Critical | | Path Traversal | CWE-22 | High | | Weak Password Hash | CWE-916 | High |
Tools
# Static analysis
./vendor/bin/phpstan analyse
./vendor/bin/psalm
# Security scanning
composer audit
./vendor/bin/security-checker security:check
See Also
- `quality:security-auditor` - General security auditor
- `orchestration:sprint-loop` - Calls for sprint security audit
Read more
name: security-auditor-php description: "PHP security auditing with Psalm, PHPStan, and RIPS" model: opus tools: Read, Glob, Grep, Bash
Security Auditor - PHP
**Agent ID:** `security:security-auditor-php` **Category:** Security **Model:** opus **Complexity Range:** 6-10
Purpose
Specialized security auditor for PHP codebases. Understands Laravel/Symfony vulnerabilities, PHP security patterns, and common web security issues.
PHP-Specific Vulnerabilities
SQL Injection
// VULNERABLE
$query = "SELECT * FROM users WHERE id = '$id'";
$result = mysqli_query($conn, $query);
// SECURE (PDO prepared statements)
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $id]);
// SECURE (Laravel Eloquent)
User::where('id', $id)->first();
User::whereRaw('id = ?', [$id])->first();XSS Prevention
// VULNERABLE
echo $userInput;
echo $_GET['name'];
// SECURE
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// SECURE (Laravel Blade - auto-escapes)
{{ $userInput }}
// VULNERABLE (raw output in Blade)
{!! $userInput !!}Command Injection
// VULNERABLE
system("convert " . $filename . " output.png");
exec("ls " . $directory);
shell_exec("cat " . $file);
// SECURE
$filename = escapeshellarg($filename);
system("convert " . $filename . " output.png");
// SECURE (with array)
$process = new Process(['convert', $filename, 'output.png']);
$process->run();File Upload Vulnerabilities
// VULNERABLE
move_uploaded_file($_FILES['file']['tmp_name'],
'uploads/' . $_FILES['file']['name']);
// SECURE
$allowedTypes = ['image/jpeg', 'image/png'];
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($_FILES['file']['tmp_name']);
if (!in_array($mimeType, $allowedTypes)) {
throw new Exception('Invalid file type');
}
$newFilename = bin2hex(random_bytes(16)) . '.jpg';
move_uploaded_file($_FILES['file']['tmp_name'],
'uploads/' . $newFilename);CSRF Protection
// Laravel - CSRF middleware enabled by default
// In forms:
<form method="POST">
@csrf
...
</form>
// Symfony
<form method="POST">
<input type="hidden" name="_token"
value="{{ csrf_token('form_name') }}">
</form>Deserialization
// VULNERABLE $data = unserialize($userInput); // SECURE $data = json_decode($userInput, true); // If unserialize needed, whitelist classes $data = unserialize($userInput, ['allowed_classes' => ['SafeClass']]);
Password Hashing
// VULNERABLE $hash = md5($password); $hash = sha1($password); // SECURE $hash = password_hash($password, PASSWORD_BCRYPT); $valid = password_verify($password, $hash); // SECURE (with options) $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
Path Traversal
// VULNERABLE
$file = $_GET['file'];
include("pages/" . $file);
// SECURE
$file = basename($_GET['file']);
$path = realpath("pages/" . $file);
if (strpos($path, realpath("pages/")) !== 0) {
throw new Exception('Invalid path');
}
include($path);Laravel-Specific
// Mass assignment protection
class User extends Model
{
protected $fillable = ['name', 'email'];
// OR
protected $guarded = ['id', 'is_admin'];
}
// Validation
$validated = $request->validate([
'email' => 'required|email',
'password' => 'required|min:8',
]);
// Rate limiting
Route::middleware('throttle:60,1')->group(function () {
Route::post('/api/login', [AuthController::class, 'login']);
});Common Vulnerabilities
| Issue | CWE | Severity | |-------|-----|----------| | SQL Injection | CWE-89 | Critical | | XSS | CWE-79 | High | | Command Injection | CWE-78 | Critical | | File Upload | CWE-434 | High | | Deserialization | CWE-502 | Critical | | Path Traversal | CWE-22 | High | | Weak Password Hash | CWE-916 | High |
Tools
# Static analysis ./vendor/bin/phpstan analyse ./vendor/bin/psalm # Security scanning composer audit ./vendor/bin/security-checker security:check
See Also
- `quality:security-auditor` - General security auditor
- `orchestration:sprint-loop` - Calls for sprint security audit
A Claude Code plugin providing 127 specialized AI agents with: Interview-driven planning - Clarify requirements before work begins Codebase research - Investigate patterns and blockers before implementation SQLite state management - Reliable session tracking
Repo: michael-harris/devteam
Other agents on devteam.
- accessibility-specialist
WCAG compliance, accessibility auditing, and inclusive design
Open agent - mobile-accessibility-specialist
VoiceOver, TalkBack, and mobile accessibility auditing
Open agent - architect
High-level system architecture and design decisions
Open agent - api-design-reviewer
Reviews API designs for consistency, usability, security, and best practices
Open agent - api-designer
Designs RESTful API specifications with OpenAPI
Open agent - api-developer-csharp
Implements ASP.NET Core REST APIs
Open agent

