Written in Rust  ·  MIT License  ·  51 rules

Security audits for
Laravel apps.

Fast, static, heuristic-based. Scan a Laravel project for insecure patterns, risky config, exposed secrets, and vulnerable dependencies — then ship a SARIF report to your CI pipeline.

bash
$ lsec scan . --ci --fail-on high
Laravel Security Audit CLI
© Afaan Bilal <https://afaan.dev>
[CRITICAL] env.weak-app-key APP_KEY looks weak or default .env:1
[HIGH] secrets.hardcoded-secret Hardcoded secret-like value detected config/services.php:12
[HIGH] injection.raw-sql Raw SQL query with interpolated input app/Repos/UserRepo.php:47
[HIGH] auth.weak-password-hash Weak hashing primitive detected app/Models/User.php:91
[MEDIUM] http.csrf-exceptions Route excluded from CSRF middleware Http/Middleware/Csrf.php:18
[MEDIUM] http.cors-wildcard Wildcard CORS origin in production config/cors.php:5
[LOW] logging.debug-artifact Debug helper left in code app/Http/Controllers/Api.php:203
1 critical · 3 high · 2 medium · 1 low · 0 info
✗ CI check failed — findings above threshold (fail-on: high)
51
Security rules
8
Rule categories
3
Output formats

Built for Laravel workflows

Static analysis that understands Laravel conventions — fast enough to run on every commit.

Fast by design

Compiled Rust binary. No interpreter, no JVM warmup, no Docker pull. Scan a large Laravel app in seconds.

🔍

Laravel-aware

Rules understand Eloquent models, middleware stacks, config conventions, route definitions, and Blade templates.

🔧

CI-first

Deterministic exit codes, SARIF output for GitHub Code Scanning, and baseline suppression for managing legacy findings.

🎯

High signal

Confidence scoring per finding. Filter out noise with --min-confidence or tune thresholds per rule in config.

51 rules across 8 categories

Each category targets a distinct security surface area of a Laravel application.

env

Environment

7 rules
  • .env commit eligibility
  • APP_DEBUG in production
  • Weak or placeholder APP_KEY
  • Hardcoded DB credentials
  • HTTP APP_URL in production
  • Insecure session cookies
  • APP_ENV mismatches
auth

Authentication

8 rules
  • Routes without auth middleware
  • Auth without policy/gate checks
  • Missing Gate/Policy definitions
  • Weak hashing (md5, sha1)
  • Remember-me without expiry
  • No modern password hashing detected
  • Impersonation without gating
  • Role assignment from input
injection

Injection

7 rules
  • Raw SQL with interpolation
  • Mass assignment / missing $fillable
  • Request input in query builder
  • eval() usage
  • exec() / shell_exec()
  • unserialize() calls
  • Dynamic include/require
http

HTTP & Session

8 rules
  • CSRF exceptions
  • Session cookie security flags
  • Wildcard CORS origins
  • Hardcoded HTTP URLs in config
  • Wildcard trusted proxies
  • SSRF via user-controlled URLs
  • Cloud metadata endpoint refs
  • Exposed debug dashboards
storage

Storage

6 rules
  • User-controlled file paths
  • Upload without validation
  • Public disk exposure
  • Files in storage/app/public
  • User-controlled filenames
  • Zip-slip in archive extraction
deps

Dependencies

6 rules
  • Outdated Laravel core version
  • Known CVEs via OSV lookup
  • Vulnerability database unavailable
  • Abandoned Composer packages
  • Unsafe PHP version constraints
  • Missing composer.lock
secrets

Secrets

4 rules
  • Hardcoded secret-like values
  • Committed private keys / certs
  • URLs with embedded credentials
  • Cloud access key literals
  • Custom regex patterns (config)
logging

Logging

5 rules
  • Debug mode leaking stack traces
  • Debug/trace log level in prod
  • Sensitive data being logged
  • dd() / dump() artifacts
  • Missing auth failure logging

Download the binary

Grab the latest pre-built binary for your platform from GitHub Releases and drop it on your path — no runtime dependencies required.

# Linux (x86_64)
curl -L https://github.com/AfaanBilal/lsec/releases/latest/download/lsec-linux-x86_64.tar.gz | tar -xz
sudo mv lsec /usr/local/bin/

# Linux (ARM64)
curl -L https://github.com/AfaanBilal/lsec/releases/latest/download/lsec-linux-arm64.tar.gz | tar -xz
sudo mv lsec /usr/local/bin/

# macOS (Apple Silicon)
curl -L https://github.com/AfaanBilal/lsec/releases/latest/download/lsec-macos-arm64.tar.gz | tar -xz
sudo mv lsec /usr/local/bin/

# macOS (Intel)
curl -L https://github.com/AfaanBilal/lsec/releases/latest/download/lsec-macos-x86_64.tar.gz | tar -xz
sudo mv lsec /usr/local/bin/

# Windows — download lsec-windows-x86_64.zip from GitHub Releases and add to PATH

Or build from source

Requires a Rust toolchain.

git clone https://github.com/AfaanBilal/lsec
cd lsec
cargo build --release
cp target/release/lsec /usr/local/bin/lsec
# Or run directly without installing
cargo run -- scan /path/to/laravel-app

CLI reference

Run a scan, list rules, or manage baselines. All options can also be set in lsec.toml.

Scan
Filtering
Output
Baseline
Basic scan
lsec scan .
List all rules
lsec rules
Summary only
lsec scan . --summary
Quiet (findings only)
lsec scan . --quiet
Custom config file
lsec scan . --config ci/lsec.toml
Ignore low-confidence
lsec scan . --min-confidence 0.8
Scan only selected categories
lsec scan . --only env,secrets,deps
Skip categories
lsec scan . --skip logging,http
Scan only specific rules
lsec scan . --only-rule http.ssrf-user-url,secrets.private-key
Skip specific rules
lsec scan . --skip-rule logging.debug-artifact
Pretty terminal (default)
lsec scan . --format pretty
JSON to file
lsec scan . --format json --output report.json
SARIF for CI/GitHub
lsec scan . --format sarif --output report.sarif
CI mode, fail on high
lsec scan . --ci --fail-on high
Write baseline during scan
lsec scan . --write-baseline
Write baseline explicitly
lsec baseline write .
Use existing baseline
lsec scan . --baseline ci/lsec-baseline.json
Remove stale entries
lsec baseline prune .

Exit codes

0

Scan completed. No findings at or above the configured failure threshold.

1

--ci was enabled and at least one finding met or exceeded --fail-on.

2

CLI or runtime error (invalid path, bad config, etc).

Severity levels

Used for --fail-on. Each level includes all levels above it.

CRITICAL HIGH MEDIUM LOW INFO

Three formats, one command

Switch between human-readable output, machine-parseable JSON, and SARIF for tooling integration.

pretty

Terminal

Color-coded severity badges, two-column layout with location, explanation, remediation, and snippet context. Starts and ends with tabular summaries by severity and category.

lsec scan .
json

JSON

Machine-readable output with scan root, severity counts, category counts, and a full findings array including remediation text and confidence score. Ideal for custom tooling.

lsec scan . --format json \
  --output report.json
sarif

SARIF 2.1.0

Industry-standard format for security tool integrations. Supports GitHub Code Scanning, Azure DevOps, and IDE extensions. Includes tool metadata, rule definitions, and source locations.

lsec scan . --format sarif \
  --output report.sarif

lsec.toml

Drop an lsec.toml in your project root to persist scan settings. Override with --config <path>.

# lsec.toml

[scan]
exclude_paths  = ["vendor/", "tests/", "node_modules/", ".git/", "storage/logs/"]
fail_on        = "high"   # CI failure threshold: critical | high | medium | low | info
min_confidence = 0.7      # Global confidence floor (0.0 – 1.0)

[rules]
skip     = ["logging"]                   # Skip entire categories
skip_ids = ["logging.debug-artifact"]    # Skip specific rule IDs

# Per-rule confidence overrides (inline table — must stay on one line)
min_confidence_overrides = { "auth.missing-route-authorization" = 0.8, "logging.auth-failure-missing" = 0.55 }

# Custom secret detection patterns (regex)
custom_secrets_patterns = [
  "(?i)internal_token\\s*=\\s*['\"][A-Za-z0-9_-]{20,}['\"]",
  "(?i)acme_live_[A-Za-z0-9]{24,}"
]

[scan]

exclude_paths — path prefixes excluded from the project walk.

fail_on — default CI severity threshold.

min_confidence — global confidence floor before reporting.

[rules]

skip — suppress entire categories.

skip_ids — suppress individual rules by exact ID.

custom_secrets_patterns — add your own regex patterns for secret detection.

min_confidence_overrides — per-rule confidence floors.

Gate every deploy

Wire lsec into your pipeline in four steps. Works with GitHub Actions, GitLab CI, Bitbucket Pipelines, and any CI that supports shell commands.

1

Run lsec on your checked-out project

Point it at the Laravel root. Use --ci to enable non-zero exit on findings.

2

Load a baseline for known legacy findings

Use --baseline ci/lsec-baseline.json to suppress accepted findings without silencing new ones.

3

Archive the SARIF report

Upload report.sarif as a build artifact or to GitHub Code Scanning for inline PR annotations.

4

Fail the job on threshold breach

Exit code 1 when any finding meets or exceeds --fail-on. Exit code 0 on clean scan.

# Full CI command
lsec scan . \
  --ci \
  --fail-on high \
  --min-confidence 0.7 \
  --baseline ci/lsec-baseline.json \
  --format sarif \
  --output lsec.sarif

GitHub Code Scanning

Add this workflow to your Laravel project to get lsec findings as inline annotations on pull requests and in the repository's Security tab. Requires security-events: write permission. The upload step runs with if: always() so findings are visible even when the scan fails the job.

# .github/workflows/lsec.yml
name: lsec

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write

jobs:
  scan:
    name: Security scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download lsec
        run: |
          curl -fsSL https://github.com/AfaanBilal/lsec/releases/latest/download/lsec-linux-x86_64.tar.gz \
            | tar -xz
          chmod +x lsec

      - name: Run lsec
        run: |
          ./lsec scan . \
            --ci \
            --fail-on high \
            --format sarif \
            --output lsec.sarif

      - name: Upload to GitHub Code Scanning
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: lsec.sarif

Suppress known findings

Baseline files let you acknowledge legacy issues without silencing future ones. Each entry is fingerprinted by rule, file, and line.

📝

Write a baseline

Captures all current findings into a JSON file. Future scans suppress any finding whose fingerprint appears in the baseline.

lsec baseline write .
# or during scan:
lsec scan . --write-baseline
✂️

Prune stale entries

After fixing findings, prune removes fingerprints that no longer match any active finding, keeping the baseline file clean.

lsec baseline prune .
🔒

Use in CI

Commit the baseline and reference it in your pipeline. New findings always surface; acknowledged ones stay quiet.

lsec scan . \
  --baseline ci/lsec-baseline.json
Afaan Bilal

Afaan Bilal

Principal Software Engineer & CISO at Centiment