SuperAPI AI Directory

symfony-security-auditor

Security & auditing

View repository

Updated 10-15 14:26

About this skill

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.