Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
aradotso avatar

Symfony Security Auditor

  • 1 installs
  • 10 repo stars
  • Updated August 4, 2026
  • aradotso/security-skills

symfony-security-auditor is a skill that runs an AI multi-agent security audit of Symfony applications to detect business-logic flaws, broken access control and complex vulnerabilities.

About

An AI-powered multi-agent security auditor for Symfony applications. It runs an ingestion, mapping and audit pipeline where an adversarial attacker agent finds issues and a skeptical reviewer agent eliminates false positives, catching business-logic flaws and broken access control that traditional SAST tools miss. A Symfony developer uses it before release. It matters because authorization and workflow bugs are hard to detect statically.

  • AI multi-agent auditor for Symfony apps that catches flaws SAST tools miss
  • Finds broken access control, missing Voters/IsGranted, IDOR and business-logic bugs
  • Outputs console, JSON, SARIF or HTML and is provider-agnostic via symfony/ai

Symfony Security Auditor by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #55 of 65 PHP & Laravel skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

symfony-security-auditor capabilities & compatibility

Needs an LLM API key; balanced profile ~$2-5/project, thorough ~$10-20/project, or free with local Ollama.

Capabilities
security audit · access control testing · sast
Works with
github · gitlab · anthropic · openai
Use cases
security audit · code review · ci cd
Runs
Runs locally
Pricing
Bring your own API key
From the docs

What symfony-security-auditor says it does

an AI-powered multi-agent security auditor for Symfony applications that catches vulnerabilities traditional SAST tools miss
SKILL.md
adversarial Attacker agent finds issues, skeptical Reviewer agent eliminates false positives over up to 3 iterations
SKILL.md
npx skills add https://github.com/aradotso/security-skills --skill symfony-security-auditor

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars10
Last updatedAugust 4, 2026
Repositoryaradotso/security-skills

What it does

Run an AI multi-agent security audit of a Symfony app to find broken access control, IDOR and business-logic flaws.

Who is it for?

Symfony teams auditing for authorization and business-logic vulnerabilities before release.

Skip if: Non-Symfony/PHP applications or dependency supply-chain scanning.

When should I use this skill?

You want to audit a Symfony codebase for access-control and business-logic flaws using LLMs.

What you get

A Symfony security audit report (SARIF/HTML/JSON) with attacker-found, reviewer-verified findings.

  • Security audit report (console/JSON/SARIF/HTML)
  • Baseline file for suppressing known findings

By the numbers

  • 3-stage pipeline (ingestion, mapping, audit)
  • Up to 3 review iterations
  • 3 profiles (fast, balanced, thorough)

Files

SKILL.mdMarkdownGitHub ↗

Symfony Security Auditor Skill

Skill by ara.so — Security Skills collection.

What It Does

symfony-security-auditor is an AI-powered multi-agent security auditor for Symfony applications that catches vulnerabilities traditional SAST tools miss:

  • Business logic flaws — authorization bypasses, workflow violations
  • Broken access control — missing #[IsGranted], missing Voters, IDOR
  • Complex injection chains — multi-file SQL injection, XSS, deserialization
  • Mass assignment — unsafe MapRequestPayload, form handling
  • Symfony-specific issues — firewall misconfigurations, unsafe Twig, Messenger handler flaws

Uses a three-stage pipeline: 1. Ingestion — scans .php, .twig, .yaml, .xml recursively 2. Mapping — classifies Controllers, Entities, Voters, Forms, Routes 3. Audit — adversarial Attacker agent finds issues, skeptical Reviewer agent eliminates false positives over up to 3 iterations

Outputs to console, JSON, SARIF (GitHub Code Scanning / GitLab Security Dashboard), or HTML.

Provider-agnostic via symfony/ai — works with Claude, GPT, Gemini, Mistral, Llama, DeepSeek, Ollama.

Installation

With Symfony Flex (automatic setup)

composer require --dev vinceamstoutz/symfony-security-auditor

Flex automatically:

  • Registers the bundle in config/bundles.php for dev and test environments
  • Creates config/packages/symfony_security_auditor.yaml with defaults

Install an AI platform bridge

# Anthropic Claude (recommended)
composer require symfony/ai-anthropic-platform

# Or OpenAI GPT
composer require symfony/ai-openai-platform

# Or Google Gemini
composer require symfony/ai-google-gemini-platform

# Or Mistral
composer require symfony/ai-mistral-platform

# Or Ollama (local)
composer require symfony/ai-ollama-platform

Configure the AI platform

Create or edit config/packages/ai.yaml:

# Anthropic Claude
ai:
  platform:
    anthropic:
      api_key: '%env(ANTHROPIC_API_KEY)%'

# Or OpenAI GPT
ai:
  platform:
    openai:
      api_key: '%env(OPENAI_API_KEY)%'

# Or Google Gemini
ai:
  platform:
    google_gemini:
      api_key: '%env(GOOGLE_GEMINI_API_KEY)%'

# Or Ollama (local, no API key)
ai:
  platform:
    ollama:
      base_url: 'http://localhost:11434'

Set environment variables in .env.local:

ANTHROPIC_API_KEY=sk-ant-api03-...
# Or
OPENAI_API_KEY=sk-proj-...
# Or
GOOGLE_GEMINI_API_KEY=...

Manual setup (without Flex)

Register bundles in config/bundles.php:

return [
    // ...
    Symfony\AI\AiBundle\AiBundle::class => ['all' => true],
    VinceAmstoutz\SymfonySecurityAuditor\SymfonySecurityAuditorBundle::class => ['dev' => true, 'test' => true],
];

Create config/packages/symfony_security_auditor.yaml:

symfony_security_auditor:
    model: 'claude-opus-4-8'
    profile: 'balanced' # fast, balanced, or thorough

Configuration

Basic configuration

Edit config/packages/symfony_security_auditor.yaml:

symfony_security_auditor:
    # Model name (provider-specific)
    model: 'claude-opus-4-8' # or 'gpt-4o', 'gemini-2.0-flash-exp', etc.
    
    # One-knob preset: fast, balanced (default), or thorough
    profile: 'balanced'
    
    # Manual tuning (overrides profile)
    max_review_iterations: 2
    enable_attacker_tools: true
    enable_reviewer_tools: false
    enable_poc_generation: false
    enable_concurrent_review: false
    enable_escalation: false
    chunk_token_limit: 8000
    enable_lean_prescan: false

Split-model configuration (cost optimization)

Use a powerful model for attack, cheap model for review:

symfony_security_auditor:
    attacker_model: 'claude-opus-4-8'
    reviewer_model: 'claude-haiku-4'

Cuts cost ~20× while maintaining high detection rate.

Rate limiting

symfony_security_auditor:
    rate_limit:
        requests_per_minute: 50
        tokens_per_minute: 80000

Profiles explained

# Fast: minimal iterations, no tools, no PoC (~$0.50/project)
profile: 'fast'

# Balanced: 2 iterations, attacker tools, selective PoC (~$2-5/project)
profile: 'balanced'

# Thorough: 3 iterations, all tools, PoC for all high-severity (~$10-20/project)
profile: 'thorough'

Key Commands

Run security audit

# Audit current directory
bin/console audit:run

# Audit specific project
bin/console audit:run /path/to/project

# Dry run (estimate cost, no actual audit)
bin/console audit:run --dry-run

# Output formats
bin/console audit:run --format console  # human-readable (default)
bin/console audit:run --format json --output report.json
bin/console audit:run --format sarif --output report.sarif
bin/console audit:run --format html --output report.html

# Diff mode (audit only changed files since branch)
bin/console audit:run --since=main

# Baseline suppression
bin/console audit:run --generate-baseline  # creates baseline.json
bin/console audit:run --baseline baseline.json  # suppress known findings

# Verbose output
bin/console audit:run -v   # show progress
bin/console audit:run -vv  # show API calls
bin/console audit:run -vvv # debug mode

Exit codes

  • 0 — no vulnerabilities found
  • 1 — vulnerabilities found
  • 2 — audit failed (error)

Real-World Usage Examples

Example 1: Basic security audit

# Install
composer require --dev vinceamstoutz/symfony-security-auditor
composer require symfony/ai-anthropic-platform

# Configure (add to .env.local)
echo "ANTHROPIC_API_KEY=sk-ant-api03-..." >> .env.local

# Run
bin/console audit:run

# Output:
# ┌─────────────────────────────────────────────────────────
# │ HIGH: Missing Authorization Check in Delete Action
# ├─────────────────────────────────────────────────────────
# │ File: src/Controller/PostController.php:42
# │ Category: Broken Access Control
# │
# │ The delete() method allows any authenticated user to
# │ delete any post without checking ownership.
# │
# │ Recommendation:
# │ Add #[IsGranted('POST_DELETE', 'post')] or create a Voter
# └─────────────────────────────────────────────────────────

Example 2: CI integration with SARIF output

# Generate SARIF for GitHub Code Scanning
bin/console audit:run \
    --format sarif \
    --output symfony-security.sarif \
    --since=main

# Upload to GitHub
gh api repos/{owner}/{repo}/code-scanning/sarifs \
    -F sarif=@symfony-security.sarif \
    -F commit_sha=$(git rev-parse HEAD) \
    -F ref=refs/heads/main

Example 3: Split-model configuration for cost savings

# config/packages/symfony_security_auditor.yaml
symfony_security_auditor:
    attacker_model: 'claude-opus-4-8'  # powerful, expensive
    reviewer_model: 'claude-haiku-4'    # fast, cheap
    profile: 'balanced'
bin/console audit:run --dry-run
# Estimated cost: $2.34 (vs $45.67 with single Opus model)

bin/console audit:run

Example 4: Baseline workflow for existing projects

# First run: generate baseline of known issues
bin/console audit:run --generate-baseline

# This creates baseline.json with all findings
# Review and accept known issues, then commit baseline.json

# Subsequent runs: only report new issues
bin/console audit:run --baseline baseline.json

# Exit code 0 if only baseline findings
# Exit code 1 if new findings detected

Example 5: HTML report for stakeholders

bin/console audit:run \
    --format html \
    --output security-report-$(date +%Y%m%d).html

# Generates self-contained HTML report with:
# - Executive summary
# - Findings grouped by severity
# - File/line locations
# - Recommendations
# - PoCs (if enabled)

Common Patterns

Pattern 1: GitHub Actions workflow

Create .github/workflows/security-audit.yml:

name: Security Audit
on:
  schedule:
    - cron: '0 2 * * *'  # nightly at 2 AM
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      security-events: write  # for SARIF upload
      contents: read
    steps:
      - uses: actions/checkout@v4
      
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      
      - run: composer install --no-dev
      
      - uses: vinceamstoutz/symfony-security-auditor@1.10.0
        with:
          format: sarif
          output: symfony-security.sarif
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: symfony-security.sarif
          category: symfony-security-auditor

Pattern 2: GitLab CI pipeline

Create .gitlab-ci.yml:

security_audit:
  stage: test
  image: php:8.3
  script:
    - composer install --dev
    - bin/console audit:run --format sarif --output symfony-security.sarif
  artifacts:
    reports:
      sast: symfony-security.sarif
  variables:
    ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
  only:
    - schedules

Pattern 3: Pull request diff audit

# .github/workflows/pr-audit.yml
name: PR Security Audit
on: pull_request

jobs:
  audit-diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history for --since
      
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      
      - run: composer install --dev
      
      - name: Audit changed files
        run: |
          bin/console audit:run \
            --since=origin/${{ github.base_ref }} \
            --format json \
            --output audit.json
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      
      - name: Comment on PR
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const audit = JSON.parse(fs.readFileSync('audit.json'));
            const comment = `## 🔒 Security Audit Found Issues\n\n${audit.summary}`;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: comment
            });

Pattern 4: Programmatic usage in PHP

<?php

namespace App\Service;

use VinceAmstoutz\SymfonySecurityAuditor\Application\Service\AuditOrchestrator;
use VinceAmstoutz\SymfonySecurityAuditor\Domain\ValueObject\AuditOptions;

class SecurityAuditService
{
    public function __construct(
        private AuditOrchestrator $orchestrator
    ) {}

    public function auditProject(string $projectPath): array
    {
        $options = new AuditOptions(
            projectPath: $projectPath,
            format: 'json',
            dryRun: false,
            since: null,
            baseline: null
        );

        $result = $this->orchestrator->execute($options);

        return [
            'vulnerabilities' => $result->getVulnerabilities(),
            'summary' => $result->getSummary(),
            'cost' => $result->getCost(),
        ];
    }
}

Pattern 5: Custom vulnerability filters

<?php

namespace App\Security;

use VinceAmstoutz\SymfonySecurityAuditor\Domain\Model\Vulnerability;

class VulnerabilityFilter
{
    public function filterCritical(array $vulnerabilities): array
    {
        return array_filter(
            $vulnerabilities,
            fn(Vulnerability $v) => $v->getSeverity() === 'CRITICAL'
        );
    }

    public function filterByCategory(array $vulnerabilities, string $category): array
    {
        return array_filter(
            $vulnerabilities,
            fn(Vulnerability $v) => $v->getCategory() === $category
        );
    }

    public function excludeAccepted(array $vulnerabilities, array $acceptedIds): array
    {
        return array_filter(
            $vulnerabilities,
            fn(Vulnerability $v) => !in_array($v->getId(), $acceptedIds)
        );
    }
}

Troubleshooting

Issue: "Model not found" error

Cause: Incorrect model name for the provider.

Solution: Check provider-specific model names:

# Anthropic Claude
model: 'claude-opus-4-8'      # or 'claude-sonnet-4', 'claude-haiku-4'

# OpenAI
model: 'gpt-4o'                # or 'gpt-4-turbo', 'gpt-3.5-turbo'

# Google Gemini
model: 'gemini-2.0-flash-exp'  # or 'gemini-1.5-pro'

# Mistral
model: 'mistral-large-latest'  # or 'mistral-small-latest'

# Ollama (local)
model: 'llama3.1'              # or 'codellama', 'deepseek-coder'

Issue: High API costs

Solution 1: Use split-model configuration:

symfony_security_auditor:
    attacker_model: 'claude-opus-4-8'
    reviewer_model: 'claude-haiku-4'  # ~20× cheaper

Solution 2: Enable lean prescan to drop low-risk files:

symfony_security_auditor:
    enable_lean_prescan: true

Solution 3: Use fast profile for initial scans:

symfony_security_auditor:
    profile: 'fast'

Solution 4: Audit only changed files:

bin/console audit:run --since=main

Issue: Rate limit errors (429)

Solution: Configure rate limits:

symfony_security_auditor:
    rate_limit:
        requests_per_minute: 50    # adjust per provider limits
        tokens_per_minute: 80000

Issue: Too many false positives

Solution 1: Use baseline suppression:

bin/console audit:run --generate-baseline
# Review baseline.json, remove false positives
bin/console audit:run --baseline baseline.json

Solution 2: Increase review iterations:

symfony_security_auditor:
    max_review_iterations: 3  # more iterations = fewer false positives

Solution 3: Use a stronger reviewer model:

symfony_security_auditor:
    reviewer_model: 'claude-opus-4-8'  # instead of haiku

Issue: Missing vulnerabilities

Solution 1: Enable all detection features:

symfony_security_auditor:
    profile: 'thorough'
    enable_attacker_tools: true
    enable_reviewer_tools: true

Solution 2: Increase chunk token limit for complex files:

symfony_security_auditor:
    chunk_token_limit: 16000  # default is 8000

Solution 3: Disable lean prescan:

symfony_security_auditor:
    enable_lean_prescan: false

Issue: Audit runs slowly

Solution 1: Enable concurrent review:

symfony_security_auditor:
    enable_concurrent_review: true

Solution 2: Use faster models:

symfony_security_auditor:
    model: 'claude-haiku-4'  # or 'gpt-4o-mini', 'gemini-2.0-flash-exp'

Solution 3: Reduce review iterations:

symfony_security_auditor:
    max_review_iterations: 1

Issue: SARIF upload fails on GitHub

Cause: Missing security-events: write permission.

Solution: Add permission to workflow:

permissions:
  security-events: write
  contents: read

Issue: Ollama connection refused

Cause: Ollama server not running.

Solution: Start Ollama server:

ollama serve

# In another terminal, pull model
ollama pull llama3.1

# Then run audit
bin/console audit:run

Issue: "No findings" on project with known vulnerabilities

Cause: Model not sophisticated enough, or context window too small.

Solution: Use a more powerful model:

symfony_security_auditor:
    model: 'claude-opus-4-8'  # best detection rate
    profile: 'thorough'

Issue: Cost estimation is inaccurate

Cause: Provider doesn't support prompt caching, or cache not warm.

Solution: Run --dry-run twice:

# First run: no cache
bin/console audit:run --dry-run
# Estimated: $12.50

# Second run: cache warm (Anthropic only)
bin/console audit:run --dry-run
# Estimated: $1.80 (90% input tokens cached)

Advanced Features

Enable PoC generation for high-severity findings

symfony_security_auditor:
    enable_poc_generation: true
    poc_severity_threshold: 'HIGH'  # HIGH or CRITICAL

Output includes concrete reproduction steps:

{
  "poc": {
    "type": "curl",
    "command": "curl -X DELETE https://app.example.com/api/posts/123 -H 'Authorization: Bearer USER_TOKEN'",
    "expected": "Post deleted without ownership check"
  }
}

Custom advisory feeds

Implement AdvisoryProviderInterface:

<?php

namespace App\Security;

use VinceAmstoutz\SymfonySecurityAuditor\Domain\Service\AdvisoryProviderInterface;

class CustomAdvisoryProvider implements AdvisoryProviderInterface
{
    public function lookup(string $package, string $version): ?array
    {
        // Query your internal vulnerability database
        return [
            'id' => 'INTERNAL-2024-001',
            'title' => 'Custom vulnerability',
            'severity' => 'HIGH',
        ];
    }
}

Register in services.yaml:

services:
    App\Security\CustomAdvisoryProvider:
        decorates: VinceAmstoutz\SymfonySecurityAuditor\Domain\Service\AdvisoryProviderInterface

Content-hash caching

Automatically enabled — identical code chunks skip LLM:

# First run
bin/console audit:run
# 50 chunks analyzed, cost: $5.00

# No code changes
bin/console audit:run
# 50 chunks cached, cost: $0.00

Cache stored in var/cache/dev/symfony_security_auditor/.

---

Tip: For best results, combine this auditor with PHPStan (type safety), Psalm Security (taint analysis), and Dependabot (dependency CVEs). This auditor excels at business logic flaws that static analysis cannot detect.

Related skills

FAQ

How does the audit avoid false positives?

An adversarial Attacker agent finds issues and a skeptical Reviewer agent eliminates false positives over up to 3 iterations.

Which LLM providers work?

It is provider-agnostic via symfony/ai and works with Claude, GPT, Gemini, Mistral, Llama, DeepSeek and Ollama.

PHP & Laravelauditappsec

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.