Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Security Auditor Laravel Package

vinceamstoutz/symfony-security-auditor

A Symfony security auditor that scans your app's security configuration and flags misconfigurations, weak settings, and risky access rules. Get actionable reports to harden firewalls, authentication, and authorization before deployment.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What this is. symfony-security-auditor is a PHP security-audit tool distributed three ways: a Symfony bundle (bin/console), a standalone binary, and a GitHub Action, now also exposing an MCP server (mcp:serve). It performs AI-assisted static analysis of application security posture — routes, access control coverage, secrets, custom risk patterns — by driving LLM providers.

Architecture fit. The critical finding for us: this is not a Laravel package. There is no service provider, no publishable config, no Blade/queue/Octane integration, and the analysis model is explicitly Symfony-shaped (YAML routing, security.yaml, access_control, when@test config folders). The realistic Laravel integration surface is the CLI contract — exit codes, stdout/stderr separation, github output format, --output artifacts — plus the MCP server for developer-tooling and agent workflows. Treat it as a CI security scanner that happens to be written in PHP, not as an application dependency.

What 1.21.0 actually changes (architecture-relevant):

  • CI contract becomes explicit. --fail-on-incomplete → exit code 3 when any file is not fully analyzed (including with --generate-baseline); warning to stderr when the report goes to stdout; GitHub Action gains fail-on-incomplete input and complete output. This is the single most valuable change for us: it converts "green build" into "clean vs. couldn't-tell," which is the difference between a scanner and a security control.
  • Provider abstraction matured. provider: <platform>.<instance> with instances for generic, openresponses, azure, bedrock, cache, failover; generic.<instance> for any OpenAI-compatible gateway behind a custom URL/token; init --base-url/--endpoint/--no-api-key; Bedrock via Mantle routes with SigV4 signing; new symfony/ai 0.14 platforms (Fireworks, Together, Venice, Eden AI, TypeSafe, Higgsfield). Translation: we can point it at our approved enterprise gateway instead of a public vendor — that is usually the unlock for security-team sign-off.
  • Credential handling became operable. Owner-only credentials.json, auth:set/status/remove, %env(file:VAR)% for Docker/K8s secrets and systemd LoadCredential=, env var still wins, masked preview + fingerprint only. Also removed the paste-ready export line from init (shell-history leak). This is genuine production readiness, not a nicety.
  • Billing attribution corrected. Per-call billing now follows the answering model (gateway routing, failover, dated aliases), with additive billed_models alongside by_model. Without this, every cost report behind an openrouter/auto-style gateway was quietly wrong.
  • Config trust boundary hardened. Project config (.symfony-security-auditor.yaml) may no longer set cache, privacy, audit.custom_skills, scan.secret_scrubbing, scan.custom_risk_patterns, may not loosen audit.budget, may not use %env()%/%name%/%%, may not be a symlink, models must be plain IDs, control chars / :: / ##[ refused, 10k-value expansion cap. Sound design (a repo shouldn't be able to weaken or read its own audit) — but see risk below.
  • Security-correctness fixes. access_control rules the production app never loads are LACKS_ACCESS_CHECK again, with the prompt now labeling coverage as repo-YAML-read rather than verified; secret scrubber now catches SECRETKEY=, APIKEY=, DBPASS=, PGPASSWORD=, PASS/PW suffixes while leaving MONKEY/MAX_PW_LENGTH/BYPASS_CACHE alone; GitHub Actions logs defuse :: and ##[ workflow-command injection.

Technical risks:

  1. Platform mismatch (highest). The differentiating analysis is Symfony-semantics-aware. On a Laravel codebase, expect meaningful value from secret scanning, dependency/config hygiene and LLM-assisted review, and thin value from route/access-control coverage. Set that expectation before the pilot or the tool will be judged as failing.
  2. Semver hygiene. A minor bump (1.20 → 1.21) carries changes that will hard-fail existing runs: forbidden project-config keys, banned %env()% references, symlinked project config refused, model options (?temperature=…) no longer accepted in project config. Anyone on ^1.21 gets this automatically. Arguably 2.0.0 material.
  3. New exit code 3 alters the CI contract; any wrapper or dashboard keyed on 1/0 needs updating, and 3 must not be conflated with "vulnerabilities found."
  4. Billing semantics change shifts cost attribution for any team behind a router/failover; downstream cost dashboards need review.
  5. Composer conflict surface. The bundle's symfony/* constraints can collide with the symfony/* transitive versions Illuminate already pulls into a Laravel app. Verify in a scratch branch; prefer the standalone binary/PHAR for Laravel to sidestep this entirely.
  6. init downloads bridges at init time — network egress and artifact provenance in locked-down CI.
  7. MCP server is new attack surface and a governance question (what an agent may invoke, with which credentials).
  8. Single-maintainer concentration — every PR in this release is by one author, numbering already at #397. Bus factor is the real dependency risk, not the code.

Key questions for the maintainer:

  • Is there a Laravel/Illuminate integration or roadmap? If not, is the standalone binary the officially supported path for non-Symfony PHP apps?
  • Does the GitHub Action emit SARIF for code scanning, and is --output stable/versioned?
  • Is --fail-on-incomplete intended as the default recommendation for gating, or opt-in only?
  • What is the determinism story? With LLM-backed analysis, how do we reproduce a finding, and is the cache platform supported for that?
  • Is audit.budget enforceable org-wide from user config, and what happens on breach — stop, degrade, or continue?
  • Licensing/support model, and is there a security policy for the credential store and MCP server?

Integration Approach

Stack fit. For a Laravel estate, integrate at the edge, not into the app:

  • CI: standalone binary (or PHAR) invoked as a build step, with github output or --output artifacts. Zero runtime coupling to Laravel.
  • Developer loop: mcp:serve from the standalone binary, wired into the IDE/agent tooling, read-only.
  • Avoid: adding the bundle as a require-dev in Laravel apps until a Composer resolution test proves no symfony/* conflict.

Migration path. There is no prior assessment, so this is net-new adoption; sequence for low blast radius:

  1. Spike (1–2 weeks). Run against two representative repos — one Laravel monolith, one Symfony service if we have one — and score finding relevance by category. Decide before rollout whether Laravel signal justifies the cost.
  2. Advisory mode. Run in CI without gating; capture reports and establish baselines. Measure cost per repo per run and per-finding token spend.
  3. Gate. Enable --fail-on-incomplete / fail-on-incomplete: true and consume the complete output. Gate on new findings over baseline only.
  4. Credentials. auth:set on developer machines; %env(file:VAR)% for Docker/K8s; never credentials.json on shared runners. Confirm env-var precedence behaves as documented in our runners.
  5. MCP. Enable last, scoped to read-only audit invocation with a dedicated low-privilege key.

Compatibility work before any upgrade or first pin:

  • Pin exact minor. Given the semver gap, 1.21.0 exact, not ^1.21.
  • Inventory config. Grep every repo for .symfony-security-auditor.yaml and move cache, privacy, audit.custom_skills, scan.secret_scrubbing, scan.custom_risk_patterns, any audit.budget loosening, any %env()%/%name%/%%, and any model options into the shared user/org config. Anything left in project config is now a hard stop.
  • Check for symlinked project configs in repos that templated it from a shared location — refused before read.
  • Exit-code audit. Find every script, wrapper action and dashboard rule that inspects this tool's exit status.
  • CI-provider check. The ::/##[ defusing is GitHub Actions-specific; if we run it on GitLab/Jenkins, sanitization of report text is on us.
  • Pin the bridge set and record which provider platforms we permit, so init can't pull an unapproved bridge.

Sequencing recommendation: pin and lock → config migration → CI contract (exit 3 + complete) → credential story → MCP. Do not enable gating before step 3 completes; a scanner that fails the build on "couldn't analyze" without baseline discipline will be disabled by the first annoyed team.

Operational Impact

Maintenance. High cadence and a single-author change stream: budget a monthly upgrade review with a named owner (~0.2 FTE platform/security engineer). Treat every minor release as potentially breaking and read the notes — the project-config hardening in this very release proves the version number is not a reliable risk signal. Pin, upgrade deliberately, never float.

Support. Community OSS, no vendor SLA. Internal support model: one owner for upgrades and config, plus a triage rota for findings. False positives need an explicit path — use baselines (--generate-baseline) rather than a repo-level suppression key, since the suppressive keys are now deliberately disallowed in project config. That's the right control, but it means suppression is a user/org-config action with a review trail, which must be documented or it becomes shadow IT.

Scaling. Two axes:

  • Cost/latency scale with repo size and finding count because analysis is LLM-backed. Enforce audit.budget centrally in user/org config (project config can no longer loosen it — a genuine win for chargeback discipline). Track spend via billed_models now that gateway routing is billed correctly, and reconcile by_model vs billed_models to catch routing surprises.
  • Coverage scales poorly on large repos: expect Audit incomplete on monoliths. The complete output is the scaling guardrail — publish it to a dashboard so "incomplete" is visible as a distinct state, never silently green.

Failure modes to instrument:

Mode Signal Required handling
No/invalid API key No API key available. at run open Treat as infra failure, not a clean pass; alert
Partial analysis exit 3, Audit incomplete: N file(s) Distinct CI state; alert on trend, not just failure
Provider failover / alias billed_models differs from by_model Cost anomaly review
Project config rejected hard stop naming the key One repo can break the whole pipeline — config changes need review ownership
Log injection ::…, ##[ in report text Covered on GHA only; sanitize ourselves elsewhere
Credential exposure masked preview + fingerprint Ban credentials.json on shared runners; prefer %env(file:VAR)%

Ramp-up. Low to run (one CLI call), moderate to interpret (someone must triage security findings in PHP/Laravel semantics), higher to own (credentials, config policy, provider/bridge approvals, cost). Plan for a half-day enablement for engineers and a short brief for stakeholders explaining that on Laravel the tool's route/access-control coverage is structurally thin — otherwise the pilot will read as a weak product rather than a scoped one.

Recommendation. Adopt in a time-boxed spike as a CI-adjacent control, not as a Laravel dependency: standalone binary, exact version pin, advisory first, gating only on new findings over baseline, credentials via %env(file:VAR)%, MCP last. Worth adopting for the secret-scanning/CI-gating and enterprise-gateway flexibility; explicitly not worth adopting as a Symfony-equivalent route/access-control auditor for Laravel. The 1.21.0 exit-code and credential work makes it materially more operable than its version number implies — and its config hardening makes it materially more likely to break on upgrade than its version number implies. Both facts argue for the same discipline: pin, read notes, gate on baselines.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource