Communitygithub.com

laravel-security

Bonnes pratiques de sécurité Laravel — authentification, autorisation, sécurité Eloquent, CSRF, prévention XSS, sécurité API et configurations de déploiement sécurisées.

Qu'est-ce que laravel-security ?

Cette compétence fournit un guide complet sur les bonnes pratiques de sécurité pour les applications Laravel, couvrant l'authentification, l'autorisation, la protection Eloquent, la prévention CSRF et XSS, la sécurité des API et les configurations de déploiement sécurisées. Elle est conçue pour être activée lors de la configuration de l'authentification et de l'autorisation Laravel (Sanctum, Passport, Jetstream, Breeze), de la mise en œuvre des rôles utilisateur, des permissions et des politiques, de la configuration des paramètres de sécurité de production et des variables d'environnement, de l'examen des applications Laravel pour les vulnérabilités de sécurité, du déploiement en production ou de l'écriture de requêtes et migrations Eloquent sécurisées. La compétence détaille les configurations essentielles de production telles que la désactivation du débogage, le HTTPS forcé, la sécurisation des fichiers `.env` et la validation des clés d'application. Elle approfondit l'authentification avec Sanctum pour les tokens API, la sécurité des mots de passe avec bcrypt/Argon2id et la validation, ainsi que la gestion des sessions. Pour l'autorisation, elle explique les Gates et les Politiques avec des exemples concrets d'implémentation. La sécurité Eloquent couvre la protection contre l'assignation massive, la prévention des injections SQL, le casting d'attributs et le masquage des données sensibles. CSRF et XSS sont traités avec des directives Blade sécurisées et des bibliothèques de nettoyage HTML. Enfin, elle aborde la sécurité des API avec Sanctum et les configurations de déploiement sécurisées via des middlewares et des validateurs de variables d'environnement.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/laravel-security

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Bonnes pratiques de sécurité Laravel

Directives de sécurité complètes pour les applications Laravel afin de se protéger contre les vulnérabilités courantes.

Quand activer

  • Configuration de l'authentification et de l'autorisation Laravel (Sanctum, Passport, Jetstream, Breeze)
  • Mise en œuvre des rôles utilisateur, permissions et politiques
  • Configuration des paramètres de sécurité de production et des variables d'environnement
  • Révision des applications Laravel pour les vulnérabilités de sécurité
  • Déploiement d'applications Laravel en production
  • Écriture de requêtes et migrations Eloquent sécurisées

Configuration de production

Paramètres de production essentiels

// config/app.php
'env' => env('APP_ENV', 'production'),
'debug' => (bool) env('APP_DEBUG', false), // CRITIQUE : Jamais true en production
'key' => env('APP_KEY'), // Doit être défini : php artisan key:generate

// config/session.php
'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax',

// Vérifier que APP_KEY est défini au démarrage
// bootstrap/app.php ou un fournisseur de services
if (empty(config('app.key'))) {
    throw new RuntimeException('APP_KEY n\'est pas défini. Exécutez : php artisan key:generate');
}

Sécurité du fichier d'environnement

# NE JAMAIS commiter .env dans le contrôle de version
# .gitignore inclut déjà .env par défaut

# Utiliser .env.example avec des espaces réservés à la place
DB_PASSWORD=
APP_KEY=
SANCTUM_TOKEN_PREFIX=

# Valider les variables requises au démarrage
// Dans AppServiceProvider::boot()
$requiredKeys = ['app.key', 'database.connections.mysql.database', 'database.connections.mysql.username'];
foreach ($requiredKeys as $key) {
    if (empty(config($key))) {
        throw new RuntimeException("Clé de configuration requise manquante : {$key}");
    }
}

Application du HTTPS

// AppServiceProvider::boot() ou middleware
if (app()->environment('production')) {
    URL::forceScheme('https');
    request()->server->set('HTTPS', 'on');
}

// config/app.php pour les proxys de confiance (équilibreurs de charge)
// Utiliser des plages IP spécifiques — * fait confiance à tout, permettant l'usurpation X-Forwarded-*
// AWS : '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'
'trusted_proxies' => ['10.0.0.0/8', '172.16.0.0/12'],

// Forcer HTTPS en production via un middleware
// app/Http/Middleware/ForceHttps.php
public function handle($request, Closure $next)
{
    if (!$request->secure() && app()->environment('production')) {
        return redirect()->secure($request->getRequestUri());
    }
    return $next($request);
}

Authentification

Sanctum (Authentification par token API)

// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
    env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : ''
)));

'expiration' => 60 * 24, // Expiration du token en minutes (null = jamais)
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

// Émission de tokens avec des capacités
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;

// Validation des capacités sur les routes
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/orders', function () {
        // L'utilisateur doit avoir la capacité 'read'
        abort_unless(Auth::user()->tokenCan('read'), 403);
        // ...
    })->middleware('abilities:read');

    Route::post('/orders', function () {
        // L'utilisateur doit avoir la capacité 'write'
        abort_unless(Auth::user()->tokenCan('write'), 403);
        // ...
    })->middleware('abilities:write');
});

Sécurité des mots de passe

// config/hashing.php
// Par défaut, c'est bcrypt. Argon2id est plus fort.
'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12), // Augmenter pour un hachage plus fort
],

'argon' => [
    'memory' => 65536,
    'threads' => 4,
    'time' => 4,
],

// Validation du mot de passe dans RegisterRequest
public function rules(): array
{
    return [
        'password' => [
            'required',
            'confirmed',
            Password::min(12)
                ->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised(), // Vérifie haveibeenpwned
        ],
    ];
}

// Limiter le taux de tentatives de connexion
// App\Http\Controllers\Auth\AuthenticatedSessionController
protected function authenticated(Request $request, $user)
{
    if ($user->wasRecentlyLockedOut()) {
        // Notifier l'utilisateur d'une connexion suspecte
        $user->notify(new SuspiciousLoginNotification($request->ip()));
    }
}

Gestion des sessions

// config/session.php
'driver' => env('SESSION_DRIVER', 'database'), // database/redis > file
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
'encrypt' => env('SESSION_ENCRYPT', false),

// Régénérer la session lors de la connexion
// App\Http\Controllers\Auth\AuthenticatedSessionController
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate(); // CRITIQUE : empêche la fixation de session
    return redirect()->intended(RouteServiceProvider::HOME);
}

// Invalider la session lors de la déconnexion
public function destroy(Request $request): RedirectResponse
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/');
}

Autorisation

Gates

// App\Providers\AuthServiceProvider
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post): bool {
        return $user->id === $post->user_id;
    });

    Gate::define('publish-post', function (User $user): bool {
        return $user->role === 'editor' || $user->role === 'admin';
    });

    // Utiliser before() pour la substitution super-admin
    Gate::before(function (User $user, string $ability): ?bool {
        if ($user->role === 'super-admin') {
            return true; // Accorde toutes les capacités
        }
        return null; // Revenir aux vérifications normales
    });
}

// Utilisation dans les contrôleurs
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);
    // Ou : $this->authorize('update-post', $post);
    // Ou : abort_unless(Auth::user()->can('update-post', $post), 403);
    // ...
}

Politiques

// App\Policies\PostPolicy
class PostPolicy
{
    use HandlesAuthorization;

    public function viewAny(?User $user): bool
    {
        return true; // Liste publique
    }

    public function view(?User $user, Post $post): bool
    {
        return $post->is_published || ($user && $user->id === $post->user_id);
    }

    public function create(User $user): bool
    {
        return $user->hasVerifiedEmail(); // Doit d'abord vérifier l'email
    }

    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id && $post->created_at->diffInDays(now()) <= 30;
    }

    public function restore(User $user, Post $post): bool
    {
        return $user->role === 'admin';
    }

    public function forceDelete(User $user, Post $post): bool
    {
        return $user->role === 'super-admin';
    }
}

// Enregistrer dans AuthServiceProvider
protected $policies = [
    Post::class => PostPolicy::class,
];

// Utilisation dans les contrôleurs
public function show(Post $post): View
{
    $this->authorize('view', $post);
    return view('posts.show', compact('post'));
}

// Utilisation dans Blade
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Modifier</a>
@endcan

@cannot('update', $post)
    <span>Vous ne pouvez pas modifier ce post</span>
@endcannot

Autorisation par middleware

// Utiliser le middleware dans les routes
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

Route::get('/posts/create', [PostController::class, 'create'])
    ->middleware('can:create,App\Models\Post');

// Middleware d'autorisation personnalisé
// app/Http/Middleware/CheckRole.php
class CheckRole
{
    public function handle(Request $request, Closure $next, string $role): mixed
    {
        if (!$request->user() || $request->user()->role !== $role) {
            abort(403, 'Non autorisé. Cette zone nécessite le rôle : ' . $role);
        }
        return $next($request);
    }
}

// Enregistrer dans Kernel
protected $routeMiddleware = [
    'role' => \App\Http\Middleware\CheckRole::class,
];

// Utilisation dans les routes
Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
});

Sécurité Eloquent

Protection contre l'assignation massive

// MAUVAIS : $guarded = [] permet à TOUTES les colonnes d'être assignées en masse
// NE JAMAIS utiliser $guarded = [] en production

// BON : Liste blanche des attributs fillable
final class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'phone',
        'avatar',
    ];
    // NE JAMAIS ajouter 'role', 'is_admin', 'is_verified' ici
}

// BON : Contrôler explicitement quels champs peuvent être remplis dans les requêtes
public function store(StoreUserRequest $request): RedirectResponse
{
    $user = User::create($request->safe()->only([
        'name', 'email', 'phone', 'avatar'
    ]));
    // $request->safe() utilise uniquement les données validées
    // $request->only() n'est PAS sûr seul sans règles de validation
}

// MAUVAIS : Créer un utilisateur avec les données de requête directement
User::create($request->all()); // VULNÉRABLE à l'assignation massive !

// MEILLEUR : Utiliser des DTO pour la création
$user = User::create($request->validated()); // Uniquement les champs validés

Prévention des injections SQL

// BON : Eloquent paramètre automatiquement les requêtes
User::where('email', $userInput)->first();
User::whereRaw('email = ?', [$userInput])->first();

// BON : Query Builder paramètre également
DB::table('users')->where('email', $userInput)->first();
DB::select('SELECT * FROM users WHERE email = ?', [$userInput]);

// MAUVAIS : Interpolation de chaîne brute
DB::select("SELECT * FROM users WHERE email = '{$userInput}'"); // VULNÉRABLE !
User::whereRaw("email = '{$userInput}'")->first(); // VULNÉRABLE !

// MAUVAIS : whereRaw/orderByRaw avec entrée non échappée
User::orderByRaw($userInput); // VULNÉRABLE !
User::groupByRaw($userInput); // VULNÉRABLE !

// MAUVAIS : DB::statement avec concaténation
DB::statement("INSERT INTO users (email) VALUES ('{$userInput}')"); // VULNÉRABLE !

Casting d'attributs

final class User extends Authenticatable
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean', // Convertir en booléen empêche l'injection de chaîne
        'settings' => 'array', // Automatiquement json_encode/json_decode
        'metadata' => 'encrypted:array', // Cast crypté Laravel 11+
        'password' => 'hashed', // Hachage automatique Laravel 10+ lors de l'assignation
    ];
}

Sécurité des modèles

final class User extends Authenticatable
{
    // Masquer les attributs sensibles des réponses JSON/API
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    // Ajouter uniquement des attributs calculés sécurisés
    protected $appends = ['full_name']; // sûr
    // NE JAMAIS ajouter de données calculées sensibles
}

final class Post extends Model
{
    // Portée globale pour filtrer les enregistrements soft deleted
    use SoftDeletes;

    // Empêcher N+1 en limitant le chargement différé (mode strict optionnel)
    // AppServiceProvider::boot()
    // Model::preventLazyLoading(!app()->isProduction());
}

Protection CSRF

Protection par défaut

// CSRF Laravel est activé par défaut via le middleware VerifyCsrfToken
// app/Http/Kernel.php (protected $middlewareGroups['web'])

// Tous les formulaires POST/PUT/PATCH/DELETE doivent inclure @csrf
<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
    <button type="submit">Créer</button>
</form>

Exclusion de routes (avec précaution)

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    // Exclure uniquement les routes qui ont une protection CSRF externe (webhooks, etc.)
    protected $except = [
        'stripe/*', // Les webhooks Stripe utilisent leur propre vérification de signature
        // Éviter 'api/*' en bloc — les routes Sanctum stateful ont besoin de CSRF.
        // Exclure uniquement les routes de webhook/endpoint spécifiques sans état.
    ];
}

CSRF avec JavaScript

<meta name="csrf-token" content="{{ csrf_token() }}">

<script>
// Exemple Axios (Laravel est livré avec Axios)
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector(
    'meta[name="csrf-token"]'
).getAttribute('content');

// Exemple Fetch
fetch('/posts', {
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(data),
});
</script>

Prévention XSS

Sécurité des templates Blade

{{-- SÛR : Échappé automatiquement par Blade --}}
{{ $userInput }}

{{-- DANGEREUX : Sortie brute — NE JAMAIS utiliser avec des entrées utilisateur --}}
{!! $userInput !!}

{{-- SÛR : Utiliser {!! !!} uniquement avec du contenu de confiance que vous contrôlez --}}
{!! $trustedHtmlFromYourServer !!}

{{-- BON : Utiliser des directives d'échappement spécifiques --}}
@js($data) {{-- Encodage JSON pour JavaScript --}}
@json($data) {{-- Encodage JSON dans les templates --}}

{{-- MAUVAIS : Entrée utilisateur directe dans du HTML brut --}}
<div>{!! $user->bio !!}</div> {{-- VULNÉRABLE si l'utilisateur fournit une bio --}}

Gestion sécurisée du HTML

// Lorsque vous devez autoriser un peu de HTML, utilisez une approche de liste blanche
use HTMLPurifier; // Nécessite : composer require ezyang/htmlpurifier

public function sanitizeHtml(string $dirty): string
{
    $config = \HTMLPurifier_Config::createDefault();
    $config->set('HTML.Allowed', 'p,b,i,a[href],ul,ol,li,br');
    $config->set('URI.AllowedSchemes', ['http', 'https', 'mailto']);
    $purifier = new \HTMLPurifier($config);
    return $purifier->purify($dirty);
}

// Dans blade :
<div>{!! $sanitizedHtml !!}</div>

Sécurité API

Limitation du taux de requêtes

// app/Http/Kernel.php
protected $middlewareGroups = [
    'api' => [
        \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
    ],
];

// Sur des routes spécifiques
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/users', [UserController::class, 'index']);
});

// Limitation personnalisée
Route::middleware('throttle:10,1')->post('/login', [AuthController::class, 'login']);

// Api limiter nommé (config/app.php)
'api_limit' => [
    'max_attempts' => 100,
    'decay_minutes' => 1,
];

Sécurité CORS

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true, // Requis pour Sanctum
];

Validation des entrées API

// Utiliser des form requests pour la validation
public function store(StoreUserRequest $request): JsonResponse
{
    $validated = $request->validated();
    // ...
    return response()->json(['user' => $user], 201);
}

// Valider les paramètres de requête
Route::get('/users', function (Request $request) {
    $request->validate([
        'per_page' => ['integer', 'min:1', 'max:100'],
        'sort' => ['in:name,email,created_at'],
        'order' => ['in:asc,desc'],
    ]);
    // ...
});

// Valider les entrées JSON
Route::post('/webhook', function (Request $request) {
    $request->validate([
        'event' => ['required', 'string'],
        'data' => ['required', 'array'],
    ]);
    // ...
});

Configuration de déploiement

Sécurité de la base de données

// config/database.php
'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true, // Mode strict SQL
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
    ]) : [],
],

Sécurité du cache

// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'), // redis > file

// Utiliser le préfixe pour le cache partagé
'prefix' => env('CACHE_PREFIX', 'laravel_app'),

'redis' => [
    'client' => env('REDIS_CLIENT', 'phpredis'),
    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', 'laravel_app:'),
    ],
],

Sécurité des files d'attente

// config/queue.php
'default' => env('QUEUE_CONNECTION', 'redis'), // redis > database

'failed' => [
    'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
    'database' => env('DB_CONNECTION', 'mysql'),
    'table' => 'failed_jobs',
],

// Limiter les tentatives de jobs
// app/Jobs/ProcessPodcast.php
public $tries = 3;
public $backoff = [10, 30, 60]; // Attente exponentielle

Surveillance de la sécurité

Journalisation

// config/logging.php
'default' => env('LOG_CHANNEL', 'stack'),

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['daily', 'slack'],
        'ignore_exceptions' => false,
    ],

    'daily' => [
        'driver' => 'daily',
        'path' => storage_path('logs/laravel.log'),
        'level' => env('LOG_LEVEL', 'error'),
        'days' => 14,
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Laravel Log',
        'emoji' => ':boom:',
        'level' => env('LOG_LEVEL', 'critical'),
    ],
],

Alertes de sécurité

// app/Exceptions/Handler.php
public function register(): void
{
    $this->reportable(function (AuthenticationException $e) {
        // Notifier l'administrateur
        Log::channel('slack')->warning('Tentative de connexion échouée', [
            'ip' => request()->ip(),
            'email' => request()->input('email'),
            'user_agent' => request()->userAgent(),
        ]);
    });

    $this->reportable(function (AuthorizationException $e) {
        Log::channel('slack')->warning('Tentative d\'accès non autorisé', [
            'user_id' => auth()->id(),
            'route' => request()->route()->uri(),
            'ip' => request()->ip(),
        ]);
    });
}

Dépendances et correctifs

Audits de sécurité

# Analyser les vulnérabilités des dépendances
composer audit

# Mettre à jour les dépendances
composer update

# Vérifier la version de Laravel
php artisan --version

# Voir les mises à jour de sécurité
composer show --latest

Correctifs rapides

# Mettre à jour vers la dernière version mineure
composer update "laravel/framework" --with-all-dependencies

# Corriger une vulnérabilité spécifique
# Exemple : CVE-2023-XXXXX
composer update "vendor/package" --with-all-dependencies

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés