Read-only security audit CLI for Laravel servers. Inspects your VPS in seconds — never writes, never mutates, safe on production.
Prefer a quick overview before installing or running it? Watch the video walkthrough first.
🔴 YouTube Video Walkthrough
Full install, upgrade, verify, and uninstall instructions live in docs/install.md.
curl -fsSL https://raw.githubusercontent.com/nagi1/larainspect/master/install.sh | shThe installer auto-detects macOS vs Linux, resolves the right archive for your CPU, verifies checksums.txt, and installs larainspect into /usr/local/bin.
Useful overrides:
# install a specific release
curl -fsSL https://raw.githubusercontent.com/nagi1/larainspect/master/install.sh | VERSION=v0.1.0 sh
# install without sudo into a user bin dir
curl -fsSL https://raw.githubusercontent.com/nagi1/larainspect/master/install.sh | INSTALL_DIR="$HOME/.local/bin" shTagged releases publish prebuilt archives for:
- macOS Apple Silicon (
larainspect_macOS_arm64.tar.gz) - macOS Intel (
larainspect_macOS_x86_64.tar.gz) - Linux x86_64 (
larainspect_Linux_x86_64.tar.gz) - Linux ARM64 (
larainspect_Linux_arm64.tar.gz) - Linux ARMv7 (
larainspect_Linux_armv7.tar.gz)
Latest-download asset names are stable so users can rely on predictable install commands.
Manual examples:
# macOS Apple Silicon
curl -fsSL https://github.com/nagi1/larainspect/releases/latest/download/larainspect_macOS_arm64.tar.gz | tar -xz
sudo install -m 0755 larainspect /usr/local/bin/larainspect
# Linux x86_64
curl -fsSL https://github.com/nagi1/larainspect/releases/latest/download/larainspect_Linux_x86_64.tar.gz | tar -xz
sudo install -m 0755 larainspect /usr/local/bin/larainspectVerify it works:
larainspect versiongo install github.com/nagi1/larainspect/cmd/larainspect@latestVerify it works:
larainspect versionHomebrew tap automation is the next package-manager priority after the first public release is cut and verified.
larainspect setup
larainspect auditlarainspect setup tries to detect the hosting layout and guess the deploy user, runtime user/group, and web user/group from the host first. It only prompts when some of those identities cannot be inferred confidently, then writes them into the generated config so later findings stay context-aware.
If you already have a config file and only want to fill missing or empty host-derived values, use:
larainspect populateIf you want a config file first, start here:
larainspect init
larainspect auditOne command scans your server's filesystem permissions, Nginx config, PHP-FPM pools, php.ini runtime policy, cron jobs, queue workers, and your Laravel app source for security misconfigurations — then gives you a clear, prioritized report.
The repository includes a deliberately vulnerable Laravel demo in demo/README.md.
It is built for live demos and videos:
- vulnerable and normal Ubuntu-based Docker images side by side
larainspectinstalled in each image through the same one-line installer shown in this README- ready-to-use config at
demo/larainspect.yamland/etc/larainspect/config.yamlinside the containers - package-managed Nginx, PHP-FPM, and Supervisor with UFW intentionally present but disabled for a familiar server layout
- intentionally insecure Laravel source, public artifacts, Nginx, PHP-FPM, and Supervisor configs that map to real larainspect checks
Fast path:
cd demo
docker compose build
docker compose up -d vulnerable
docker compose exec vulnerable larainspect audit --config /etc/larainspect/config.yamlFor the full step-by-step walkthrough and presenter-friendly commands, see demo/README.md.
larainspect auditAuto-detects Laravel apps under common paths (/var/www, /srv/www) and scans the host.
larainspect audit --scope app --app-path /var/www/shopPrompts you for missing info (app path, scope) without breaking automation defaults:
larainspect audit --interactive# write a starter config in the current directory
larainspect init
# fill missing config values in an existing file without replacing values you set
larainspect populate
# detect aaPanel / Forge / DigitalOcean / cPanel / common VPS layouts,
# then persist guessed deploy/runtime/web identities
larainspect setupUse setup when you want Larainspect to generate a new tuned config for the current host. Use populate when you already have a config and want Larainspect to backfill only the missing server, Laravel, service, or identity values. Use init when you want a minimal starter file and prefer to fill in the identity policy yourself.
# JSON (pipe to jq, feed CI, store artifacts)
larainspect audit --format json
# Markdown file
larainspect audit --report-markdown-path ./audit-report.md
# JSON file
larainspect audit --report-json-path ./audit-report.json
# Both at once
larainspect audit --report-json-path ./out.json --report-markdown-path ./out.mdlarainspect audit --format json --verbosity quiet
echo $? # exit code tells you the worst severity foundlarainspect audit --scan-root /var/www --scan-root /home/deployer/apps| Area | Examples |
|---|---|
| Filesystem permissions | Owner/runtime identity split, .env exposure, world-writable paths, storage symlink boundaries |
| Nginx boundaries | Public docroot validation, dotfile deny rules, PHP passthrough scope, header hardening |
| PHP-FPM security | Pool isolation, socket permissions, php.ini/runtime flags, Nginx-to-PHP execution boundaries |
| Secrets exposure | Leaked credentials in source, .env in public, debug endpoints left enabled |
| Source config | APP_DEBUG, unsafe queue/session drivers, missing encryption keys |
| Source security | Mass assignment risks, unvalidated input, raw SQL, unsafe file handling |
| Framework heuristics | Laravel, Livewire, Filament, and admin panel misconfigurations |
| Dependency vulnerabilities | Known CVEs in Composer dependencies |
| Cron & workers | Misconfigured schedules, supervisor gaps, queue driver mismatches |
| Deploy & drift | Stale releases, uncommitted changes, permission drift after deploy |
| Network hardening | Exposed debug ports, unnecessary open services |
| Forensics | Webshell indicators, suspicious cron entries, anomalous file timestamps |
Every finding includes a severity (critical / high / medium / low / info), confidence level, evidence, and a plain-language explanation with next steps.
When evidence is missing or access is denied, larainspect reports it as an unknown — never silently skips.
Designed for CI pipelines and scripts:
| Code | Meaning |
|---|---|
0 |
Clean — no findings, no unknowns |
2 |
Usage or flag error |
10 |
Low / info risk, or unknowns only |
20 |
Medium-risk finding |
30 |
High-risk finding |
40 |
Critical-risk finding |
50 |
Audit execution failure |
larainspect audit --format json --verbosity quiet
if [ $? -ge 30 ]; then
echo "High or critical risk found — blocking deploy"
exit 1
fiLarainspect works with zero config. For recurring audits, drop a config file and it auto-loads:
To generate one instead of writing it by hand:
larainspect init
larainspect setupSearch order: larainspect.yaml → larainspect.yml → .larainspect.yaml → .larainspect.yml → larainspect.json → .larainspect.json → /etc/larainspect/config.yaml → /etc/larainspect/config.json
Or specify one explicitly:
larainspect audit --config /etc/larainspect/config.yamlExample config (larainspect.yaml):
version: 1
server:
name: laravel-production
laravel:
scope: auto
app_path: /var/www/laravel/current
scan_roots:
- /var/www
identities:
deploy_users:
- deploy
runtime_users:
- www-data
runtime_groups:
- www-data
web_users:
- www-data
web_groups:
- www-data
output:
format: terminal
verbosity: normal
rules:
disable:
- source.config # skip source config checks on this hostThe identities block is optional, but it is the preferred way to make permission, drift, runtime-boundary, and socket-boundary findings match your real deploy/runtime/web account model instead of relying on inference alone.
Identity fields:
deploy_users: release, CI, or SSH users that deploy the appruntime_users: PHP-FPM, Horizon, queue, or scheduler users that execute Laravel coderuntime_groups: groups that back those runtime processesweb_users: Nginx or Apache worker users that front the appweb_groups: groups used by the web tier
If you run larainspect setup, Larainspect will try to populate these from the host automatically and only ask for the missing values. If you already have a config, larainspect populate applies the same host inference only to missing or empty values and leaves the rest alone.
See larainspect.example.yaml for all available options.
List the normalized external security controls (OWASP, Laravel docs, platform hardening guides) that drive larainspect's checks:
larainspect controls
larainspect controls --status implemented
larainspect controls --format json--screen-reader— compact, explicit guidance for assistive tech--no-color— plain ASCII, no ANSI escapes--verbosity quiet— minimal output for focused use- JSON output is always clean on stdout
- Never writes to application code, service config, permissions, or runtime state
- Never phones home — no internet access needed during a normal audit
- Bounded execution — all shell commands are allowlisted with timeouts and output caps
- Findings, heuristics, compromise indicators, and unknowns are kept separate and explicit
go test ./...
go run ./cmd/larainspect auditSee CONTRIBUTING.md for package boundaries, testing strategy, and how to add new checks.
Planned work to expand coverage, improve usability, and make larainspect easier to validate in real-world Laravel environments:
- Build comprehensive contributing guide with development setup, testing workflows, and contribution guidelines
- Write comprehensive project documentation covering installation, upgrade paths, usage patterns, CI integration, and troubleshooting
- Add CI build pipeline with automated release workflows
- Build a project landing page and homepage for better discoverability
- Create an introduction video demonstrating the tool's usage and value
- Add a vulnerable demo Laravel project for end-to-end testing and live examples
- Include intentionally vulnerable Nginx and PHP-FPM configurations in the demo environment
- Add an HTML report format for shareable audit output
- Add richer remediation guidance per finding with concrete fix steps
- Enhance the README with more examples, screenshots, and user guides
- Add CI-oriented output formats such as SARIF or JUnit where they provide downstream value
- Add report diffing and baseline comparison workflows for recurring audits
- Add first-class package coverage for Laravel Horizon
- Add first-class package coverage for Laravel Telescope
- Add first-class package coverage for Laravel Pulse
- Expand framework and ecosystem heuristics for more Laravel deployment patterns and admin stacks
- Keep iterating on new checks and workflows based on real audit results, common Laravel hosting failures, and contributor feedback
Ahmed Nagi (nagi1) — X: nagiworks on X
Release instructions live in docs/releasing.md.
Fast path:
./scripts/release.sh v0.1.0That runs the test suite, validates .goreleaser.yml, creates an annotated tag, and pushes it to GitHub so the release workflow publishes the archives.
For a local dry run:
./scripts/release.sh --snapshotMIT
