
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)
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
What symfony-security-auditor says it does
an AI-powered multi-agent security auditor for Symfony applications that catches vulnerabilities traditional SAST tools miss
adversarial Attacker agent finds issues, skeptical Reviewer agent eliminates false positives over up to 3 iterations
npx skills add https://github.com/aradotso/security-skills --skill symfony-security-auditorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 10 |
| Last updated | August 4, 2026 |
| Repository | aradotso/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
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-auditorFlex automatically:
- Registers the bundle in
config/bundles.phpfordevandtestenvironments - Creates
config/packages/symfony_security_auditor.yamlwith 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-platformConfigure 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 thoroughConfiguration
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: falseSplit-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: 80000Profiles 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 modeExit codes
0— no vulnerabilities found1— vulnerabilities found2— 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/mainExample 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:runExample 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 detectedExample 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-auditorPattern 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:
- schedulesPattern 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× cheaperSolution 2: Enable lean prescan to drop low-risk files:
symfony_security_auditor:
enable_lean_prescan: trueSolution 3: Use fast profile for initial scans:
symfony_security_auditor:
profile: 'fast'Solution 4: Audit only changed files:
bin/console audit:run --since=mainIssue: Rate limit errors (429)
Solution: Configure rate limits:
symfony_security_auditor:
rate_limit:
requests_per_minute: 50 # adjust per provider limits
tokens_per_minute: 80000Issue: 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.jsonSolution 2: Increase review iterations:
symfony_security_auditor:
max_review_iterations: 3 # more iterations = fewer false positivesSolution 3: Use a stronger reviewer model:
symfony_security_auditor:
reviewer_model: 'claude-opus-4-8' # instead of haikuIssue: Missing vulnerabilities
Solution 1: Enable all detection features:
symfony_security_auditor:
profile: 'thorough'
enable_attacker_tools: true
enable_reviewer_tools: trueSolution 2: Increase chunk token limit for complex files:
symfony_security_auditor:
chunk_token_limit: 16000 # default is 8000Solution 3: Disable lean prescan:
symfony_security_auditor:
enable_lean_prescan: falseIssue: Audit runs slowly
Solution 1: Enable concurrent review:
symfony_security_auditor:
enable_concurrent_review: trueSolution 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: 1Issue: SARIF upload fails on GitHub
Cause: Missing security-events: write permission.
Solution: Add permission to workflow:
permissions:
security-events: write
contents: readIssue: 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:runIssue: "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 CRITICALOutput 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\AdvisoryProviderInterfaceContent-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.00Cache 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.