Communitygithub.com

laravel-security

Laravel 보안 모범 사례 — 인증, 권한 부여, Eloquent 안전성, CSRF, XSS 방지, API 보안 및 안전한 배포 구성을 제공합니다. Laravel 인증, Eloquent 안전성, CSRF, XSS, API 보안 또는 배포 설정을 검토할 때 활성화하세요.

laravel-security란 무엇인가요?

이 스킬은 Laravel 애플리케이션의 보안 설정 및 구현을 위한 포괄적인 모범 사례 가이드입니다. Laravel Sanctum, Passport, Jetstream, Breeze 등 다양한 인증 시스템 설정과 함께 사용자 역할, 권한, 폴리시를 구현하는 방법을 상세히 다룹니다. Eloquent ORM의 대량 할당 보호, SQL 인젝션 방지, 속성 캐스팅 등 데이터베이스 계층의 보안을 강화하는 방법을 포함하며, 프로덕션 환경에서 필수적인 HTTPS 강제 적용, 환경 파일 보안, 세션 관리 설정 등도 안내합니다. CSRF 토큰 검증, XSS 방지를 위한 Blade 템플릿 필터링, API 토큰 인증의 모범 사례를 제공하여 Laravel 개발자들이 안전한 웹 애플리케이션을 구축할 수 있도록 돕습니다. 지원 플랫폼: Laravel 기반 프로젝트(Cursor, Codex, Claude Code 등).

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/laravel-security

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Laravel 보안 모범 사례

일반적인 취약점으로부터 Laravel 애플리케이션을 보호하기 위한 포괄적인 보안 지침입니다.

활성화 시점

  • Laravel 인증 및 권한 부여 설정 (Sanctum, Passport, Jetstream, Breeze)
  • 사용자 역할, 권한 및 폴리시 구현
  • 프로덕션 보안 설정 및 환경 변수 구성
  • Laravel 애플리케이션의 보안 취약점 검토
  • Laravel 애플리케이션을 프로덕션 환경에 배포
  • 안전한 Eloquent 쿼리 및 마이그레이션 작성

프로덕션 구성

필수 프로덕션 설정

// config/app.php
'env' => env('APP_ENV', 'production'),
'debug' => (bool) env('APP_DEBUG', false), // 중요: 프로덕션에서는 절대 true로 설정하지 마세요
'key' => env('APP_KEY'), // 반드시 설정되어야 함: php artisan key:generate

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

// 부트 시 APP_KEY 확인
// bootstrap/app.php 또는 서비스 프로바이더
if (empty(config('app.key'))) {
    throw new RuntimeException('APP_KEY가 설정되지 않았습니다. 다음 명령을 실행하세요: php artisan key:generate');
}

환경 파일 보안

# .env를 절대 버전 관리에 커밋하지 마세요
# .gitignore는 기본적으로 .env를 포함합니다

# 대신 .env.example을 사용하여 플레이스홀더를 넣으세요
DB_PASSWORD=
APP_KEY=
SANCTUM_TOKEN_PREFIX=

# 부트 시 필수 변수 검증
// AppServiceProvider::boot()
$requiredKeys = ['app.key', 'database.connections.mysql.database', 'database.connections.mysql.username'];
foreach ($requiredKeys as $key) {
    if (empty(config($key))) {
        throw new RuntimeException("필수 구성 키가 누락되었습니다: {$key}");
    }
}

HTTPS 강제 적용

// AppServiceProvider::boot() 또는 미들웨어
if (app()->environment('production')) {
    URL::forceScheme('https');
    request()->server->set('HTTPS', 'on');
}

// config/app.php의 신뢰할 수 있는 프록시 (로드 밸런서)
// 특정 IP 범위 사용 — *은 모든 것을 신뢰하여 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'],

// 미들웨어를 통해 프로덕션에서 HTTPS 강제 적용
// 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);
}

인증

Sanctum (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, // 토큰 만료 시간(분), null = 만료 없음
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

// 능력(abilities)을 가진 토큰 발급
$token = $user->createToken('api-token', ['read', 'write'])->plainTextToken;

// 라우트에서 능력 검증
Route::middleware('auth:sanctum')->group(function () {
    Route::get('/orders', function () {
        // 사용자에게 'read' 능력이 있어야 함
        abort_unless(Auth::user()->tokenCan('read'), 403);
        // ...
    })->middleware('abilities:read');

    Route::post('/orders', function () {
        // 사용자에게 'write' 능력이 있어야 함
        abort_unless(Auth::user()->tokenCan('write'), 403);
        // ...
    })->middleware('abilities:write');
});

비밀번호 보안

// config/hashing.php
// 기본값은 bcrypt입니다. Argon2id가 더 강력합니다.
'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12), // 더 강력한 해싱을 위해 증가
],

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

// RegisterRequest에서 비밀번호 검증
public function rules(): array
{
    return [
        'password' => [
            'required',
            'confirmed',
            Password::min(12)
                ->letters()
                ->mixedCase()
                ->numbers()
                ->symbols()
                ->uncompromised(), // haveibeenpwned 확인
        ],
    ];
}

// 로그인 시도 속도 제한
// App\Http\Controllers\Auth\AuthenticatedSessionController
protected function authenticated(Request $request, $user)
{
    if ($user->wasRecentlyLockedOut()) {
        // 의심스러운 로그인에 대해 사용자에게 알림
        $user->notify(new SuspiciousLoginNotification($request->ip()));
    }
}

세션 관리

// 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),

// 로그인 시 세션 재생성
// App\Http\Controllers\Auth\AuthenticatedSessionController
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate(); // 중요: 세션 고정 방지
    return redirect()->intended(RouteServiceProvider::HOME);
}

// 로그아웃 시 세션 무효화
public function destroy(Request $request): RedirectResponse
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/');
}

권한 부여

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';
    });

    // before()를 사용한 슈퍼 관리자 재정의
    Gate::before(function (User $user, string $ability): ?bool {
        if ($user->role === 'super-admin') {
            return true; // 모든 능력 부여
        }
        return null; // 일반 검사로 폴백
    });
}

// 컨트롤러에서 사용
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);
    // 또는: $this->authorize('update-post', $post);
    // 또는: abort_unless(Auth::user()->can('update-post', $post), 403);
    // ...
}

Policies

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

    public function viewAny(?User $user): bool
    {
        return true; // 공개 목록
    }

    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(); // 이메일 인증 필요
    }

    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';
    }
}

// AuthServiceProvider에 등록
protected $policies = [
    Post::class => PostPolicy::class,
];

// 컨트롤러 사용
public function show(Post $post): View
{
    $this->authorize('view', $post);
    return view('posts.show', compact('post'));
}

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

@cannot('update', $post)
    <span>You cannot edit this post</span>
@endcannot

미들웨어 권한 부여

// 라우트에서 미들웨어 사용
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

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

// 커스텀 권한 부여 미들웨어
// 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, 'Unauthorized. 이 영역은 역할이 필요합니다: ' . $role);
        }
        return $next($request);
    }
}

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

// 라우트 사용
Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
});

Eloquent 보안

대량 할당 방지

// 나쁨: $guarded = []는 모든 컬럼을 대량 할당 허용
// 프로덕션에서 절대 $guarded = []를 사용하지 마세요

// 좋음: 채울 수 있는 속성을 허용 목록으로 지정
final class User extends Authenticatable
{
    protected $fillable = [
        'name',
        'email',
        'phone',
        'avatar',
    ];
    // 'role', 'is_admin', 'is_verified'를 절대 여기에 추가하지 마세요
}

// 좋음: 요청에서 채울 수 있는 필드를 명시적으로 제어
public function store(StoreUserRequest $request): RedirectResponse
{
    $user = User::create($request->safe()->only([
        'name', 'email', 'phone', 'avatar'
    ]));
    // $request->safe()는 검증된 데이터만 사용
    // $request->only()는 검증 규칙 없이 자체적으로 안전하지 않음
}

// 나쁨: 요청 데이터로 직접 사용자 생성
User::create($request->all()); // 대량 할당에 취약!

// 더 좋음: 생성을 위해 DTO 사용
$user = User::create($request->validated()); // 검증된 필드만

SQL 인젝션 방지

// 좋음: Eloquent는 자동으로 쿼리를 매개변수화합니다
User::where('email', $userInput)->first();
User::whereRaw('email = ?', [$userInput])->first();

// 좋음: Query Builder도 매개변수화합니다
DB::table('users')->where('email', $userInput)->first();
DB::select('SELECT * FROM users WHERE email = ?', [$userInput]);

// 나쁨: 원시 문자열 보간
DB::select("SELECT * FROM users WHERE email = '{$userInput}'"); // 취약!
User::whereRaw("email = '{$userInput}'")->first(); // 취약!

// 나쁨: 이스케이프되지 않은 입력으로 whereRaw/orderByRaw 사용
User::orderByRaw($userInput); // 취약!
User::groupByRaw($userInput); // 취약!

// 나쁨: 연결과 함께 DB::statement 사용
DB::statement("INSERT INTO users (email) VALUES ('{$userInput}')"); // 취약!

속성 캐스팅

final class User extends Authenticatable
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean', // boolean으로 캐스팅하면 문자열 주입 방지
        'settings' => 'array', // 자동으로 json_encode/json_decode
        'metadata' => 'encrypted:array', // Laravel 11+ 암호화 캐스팅
        'password' => 'hashed', // Laravel 10+ 설정 시 자동 해싱
    ];
}

모델 보안

final class User extends Authenticatable
{
    // JSON/API 응답에서 민감한 속성 숨기기
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_secret',
        'two_factor_recovery_codes',
    ];

    // 안전한 계산된 속성만 추가
    protected $appends = ['full_name']; // 안전
    // 민감한 계산된 데이터를 절대 추가하지 마세요
}

final class Post extends Model
{
    // 소프트 삭제된 레코드를 필터링하는 글로벌 스코프
    use SoftDeletes;

    // 지연 로딩을 제한하여 N+1 방지 (선택적 엄격 모드)
    // AppServiceProvider::boot()
    // Model::preventLazyLoading(!app()->isProduction());
}

CSRF 보호

기본 보호

// Laravel CSRF는 VerifyCsrfToken 미들웨어를 통해 기본적으로 활성화됨
// app/Http/Kernel.php (protected $middlewareGroups['web'])

// 모든 POST/PUT/PATCH/DELETE 폼에는 @csrf를 포함해야 함
<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
    <button type="submit">Create</button>
</form>

라우트 제외 (신중하게)

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    // 외부 CSRF 보호가 있는 라우트만 제외 (webhook 등)
    protected $except = [
        'stripe/*', // Stripe webhook은 자체 서명 검증 사용
        // 'api/*'를 무분별하게 제외하지 마세요 — stateful Sanctum 라우트는 CSRF가 필요합니다.
        // 특정 stateless webhook/엔드포인트 라우트만 제외하세요.
    ];
}

JavaScript와 함께 CSRF

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

<script>
// Axios 예제 (Laravel은 Axios와 함께 제공)
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector(
    'meta[name="csrf-token"]'
).getAttribute('content');

// 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>

XSS 방지

Blade 템플릿 보안

{{-- 안전: Blade에 의해 자동 이스케이프됨 --}}
{{ $userInput }}

{{-- 위험: 원시 출력 — 사용자 입력에 절대 사용하지 마세요 --}}
{!! $userInput !!}

{{-- 안전: {!! !!}는 제어하는 신뢰할 수 있는 콘텐츠에만 사용 --}}
{!! $trustedHtmlFromYourServer !!}

{{-- 좋음: 특정 이스케이프 지시문 사용 --}}
@js($data) {{-- JavaScript용 JSON 인코딩 --}}
@json($data) {{-- 템플릿에서 JSON 인코딩 --}}

{{-- 나쁨: 원시 HTML에서 직접 사용자 입력 --}}
<div>{!! $user->bio !!}</div> {{-- 사용자가 bio를 제공하는 경우 취약 --}}

안전한 HTML 처리

// 일부 HTML을 허용해야 하는 경우 허용 목록 접근 방식 사용
use HTMLPurifier; // 필요: 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);
}

// Blade에서:
<div>{!! $sanitizedHtml !!}</div> {{-- 소독 후 안전 --}}

// Markdown 처리
use GrahamCampbell\Markdown\Facades\Markdown;

public function renderMarkdown(string $markdown): string
{
    return Markdown::convertToHtml($markdown);
}

API 보안

속도 제한

// app/Http/Kernel.php
'api' => [
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],

// config/app.php 또는 AppServiceProvider에서 사용자 정의 속도 제한
protected function configureRateLimiting(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });

    RateLimiter::for('login', function (Request $request) {
        return Limit::perMinute(5)->by($request->ip());
    });

    RateLimiter::for('sensitive', function (Request $request) {
        return Limit::perMinute(2)->by($request->user()?->id ?: $request->ip());
    });
}

// 라우트에서 사용
Route::middleware('throttle:login')->group(function () {
    Route::post('/login', [AuthController::class, 'login']);
});

API 응답 보안

// 민감한 데이터를 노출하지 않도록 API 리소스 사용
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            // 'password', 'role' 등 민감한 데이터 제외
        ];
    }
}

// 컨트롤러에서 사용
public function show(User $user): UserResource
{
    $this->authorize('view', $user);
    return new UserResource($user);
}

// JSON 응답에서 민감한 속성을 자동으로 숨기기
protected $hidden = ['password', 'remember_token'];

CORS 구성

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', '')),
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => env('CORS_SUPPORTS_CREDENTIALS', false),
];

// 프로덕션에서 allowed_origins를 특정 도메인으로 제한
// 예: 'allowed_origins' => ['https://example.com'],
// '*'는 프로덕션에서 피해야 함

안전한 배포

환경 변수

# 필수 변수
APP_KEY=base64:... # 생성: php artisan key:generate
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com

# 데이터베이스
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=app_user
DB_PASSWORD=strong_password_here

# 세션
SESSION_DRIVER=database
SESSION_SECURE_COOKIE=true
SESSION_ENCRYPT=true

# 이메일 (SMTP)
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
[email protected]
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls

# Casthand
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis

# Sanctum
SANCTUM_STATEFUL_DOMAINS=example.com
SESSION_DOMAIN=.example.com

파일 권한

# storage 및 bootstrap/cache 디렉토리가 웹 서버에 의해 쓰기 가능한지 확인
# 소유권 설정 (Ubuntu/Debian)
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 755 storage bootstrap/cache

# 또는 그룹 기반 권한
sudo chown -R $USER:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache

# 민감한 파일 보호
chmod 640 .env
chmod 644 .gitignore

보안 헤더

// app/Http/Middleware/SecureHeaders.php
class SecureHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->header('X-Content-Type-Options', 'nosniff');
        $response->header('X-Frame-Options', 'DENY');
        $response->header('X-XSS-Protection', '1; mode=block');
        $response->header('Referrer-Policy', 'strict-origin-when-cross-origin');
        $response->header('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
        $response->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

        // Content-Security-Policy는 애플리케이션에 맞게 조정 필요
        // $response->header('Content-Security-Policy', "default-src 'self'");

        return $response;
    }
}

// Kernel.php에 등록
protected $middlewareGroups = [
    'web' => [
        // ...
        \App\Http\Middleware\SecureHeaders::class,
    ],
];

일반적인 함정

.env에 민감한 정보 노출

# 절대 하지 마세요:
git add .env  # 실수로 민감한 자격 증명 커밋

# 대신:
# .env.example을 사용하고 .env를 .gitignore에 유지하세요
# .env를 외부에서 안전하게 주입 (예: CI/CD 비밀, AWS Secrets Manager)

dd() 또는 dump()를 프로덕션에 남기기

// 절대 하지 마세요:
// 디버그 코드를 프로덕션에 커밋
dd($sensitiveData); // SQL 쿼리, 비밀번호 노출 가능

// 배포 전 코드 검토에서 확인
// AppServiceProvider::boot()에서 앱의 APP_DEBUG 확인
if (app()->environment('production')) {
    // 디버그 출력 비활성화
    Config::set('app.debug', false);
}

취약한 의존성

# 정기적으로 취약점 검사
composer audit

# 의존성 업데이트
composer update

# 보안 권고 확인
# https://github.com/advisories?query=Laravel

Laravel 보안 체크리스트

  • APP_DEBUG가 프로덕션에서 false로 설정됨
  • APP_KEY가 설정되고 안전함 (php artisan key:generate)
  • HTTPS가 강제 적용됨
  • .env가 버전 관리에 커밋되지 않음
  • 데이터베이스 자격 증명이 안전함
  • 세션 구성이 안전함 (secure, http_only, same_site)
  • CSRF 보호가 활성화됨
  • Blade 템플릿에서 입력이 이스케이프됨
  • 접근 제어에 Gates/Policies 사용
  • 대량 할당이 $fillable로 제한됨
  • 비밀번호가 bcrypt 또는 argon2id로 해싱됨
  • API 엔드포인트에 속도 제한이 있음
  • 보안 헤더가 설정됨 (X-Frame-Options, X-XSS-Protection, etc.)
  • storage 및 bootstrap/cache 권한이 안전함
  • 취약한 의존성 없음 (composer audit)
  • 로그인 시 세션이 재생성됨 (세션 고정 방지)
  • API 응답이 민감한 데이터를 숨김
  • CORS가 신뢰할 수 있는 출처로 제한됨
  • Content-Security-Policy 헤더가 구성됨
  • 이메일 확인이 활성화됨
  • 실패한 로그인 시도가 기록되고 제한됨
  • 프로덕션에서 $guarded = []가 사용되지 않음
  • 모든 사용자 입력이 검증되고 소독됨

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.

관련 스킬