Laravel CRM security audit: 16 findings and targeted remediation
A repeat assessment in my practice showed why a superficial Laravel CRM review is insufficient. Earlier fixes still left authorization boundaries, imports, exports, document processing and server access needing attention. This anonymized case separates documented changes from residual risks.

Scope and method
The stack was Laravel 12, PHP 8.2, MySQL, Redis and queues. I reviewed routes, controllers, models, migrations and actual database schema, together with policies/middleware, forms, APIs, imports/exports, files, background jobs, dependencies and infrastructure.
The method was discovery, scoping, verification and reporting. Findings were tied to behavior, source, schema or configuration; a list of suspicious constructs alone was not treated as a confirmed result.
Priority distribution
P1 — 8 findings
Priority risks covered data exposure, unsafe output, document loss and server access.
P2 — 6 findings
Further work covered internal fields, access, queues, configuration and operational reliability.
P3 — 2 findings
Planned work covered schema reproducibility and duplicate definitions.
P0 — 0 findings
No findings in this category were recorded at that assessment point. This does not establish the absence of unknown risks.
Problem classes and targeted fixes
Authorization
Related records are scoped to their owner and business context; responses are minimized. Policies and visibility rules protect records and documents.
Bulk imports
Confirmation uses stored validated state, limits and a one-time identifier. Arbitrary client payloads no longer replace the validation result.
Stored XSS and XLSX
User-controlled data is rendered safely and exported as text rather than executable formulas.
Documents and queues
Failed processing is not counted as success. Retry behavior and source retention protect documents during failures.
Secrets and configuration
Service values are excluded from JSON and serialized jobs; runtime configuration is resolved during execution.
SSH and networking
Password-based SSH was disabled and inbound access restricted. An agreed key-only root exception remains explicitly documented.
Document protection
Downloads use opaque links with random identifiers. Authentication and record-level authorization are required; storage paths and internal original names are not exposed.
Previously issued links can safely redirect to the new protected address when the document exists and the user is authorized. Invalid requests do not disclose storage locations.
What verification establishes
- During the initial stage, before development dependencies were removed, 32 unit tests and 89 assertions passed. This was not a comprehensive regression suite for all subsequent 16 findings.
- Changed PHP/Blade syntax checks and the production Vite build succeeded.
- At their respective check dates, Composer audit and npm audit reported no advisories for the reviewed dependency sets.
- A targeted XLSX runtime check confirmed user-controlled values were written as text.
- A controlled document-processing failure entered failed_jobs while preserving the source file.
- The repeat read-only pass did not run production automated tests because development dependencies were absent; complete authenticated smoke coverage across roles remained a limitation.
Resolved findings and exclusions
The repeat report records 13 closed items, one closed with an agreed exception and two excluded by the client. Application fixes were deployed. This is separate from the Docker/PHP case, where no production rollout occurred.
- Client exclusion: removing secrets and dumps from Git history. Removing them from the current tree does not erase earlier revisions.
- Client exclusion: operating-system and system-package upgrades. This residual risk is not presented as resolved.
- Agreed exception: administrative root access remains key-only.
- Historical credential rotation and complete role verification still require confirmation and are not claimed complete here.
Outcome and limitations
My deliverable combined confirmed risks, targeted fixes and explicit exceptions. The findings included operational defects affecting reliability and data, not only security vulnerabilities. It would be inaccurate to call all 16 items fully resolved.
One dependency-installation stage briefly returned HTTP 500 because autoload generation was incomplete. Completing installation restored a successful login-page check. This case therefore makes no claim of an error-free deployment.
The public case omits exploitable routes, customer data, server addresses and secrets.
Frequently asked questions
Were all 16 findings fully resolved?
No. Closed findings, an agreed exception and two client exclusions—Git-history cleanup and OS upgrades—remain distinct.
Is Composer audit sufficient?
No. Authorization, import, output and queue logic defects are not reducible to a package-advisory list.
Services, article and documented case studies
Related services and technical reading
Custom CRM developmentDiscuss the scope and approach for a similar task.
