Project Details
lsec
lsec is a Laravel security audit CLI written in Rust. It scans a Laravel
project for common security issues, insecure coding patterns, risky
configuration, exposed secrets, and dependency concerns, then reports findings
in human-readable, JSON, or SARIF form. It currently ships 61 rules across 8
categories.
Why lsec
Laravel applications often accumulate security-sensitive configuration and
framework conventions across routes, middleware, models, environment files, and
deployment settings. lsec helps surface high-signal issues early by performing
fast repository-level static checks against common Laravel security footguns.
lsec is designed to be useful for:
- local security reviews before shipping
- CI gates for Laravel repositories
- quick audits of new or inherited codebases
- exporting machine-readable findings for tooling pipelines
What It Checks
lsec currently groups checks into 8 categories:
envauthinjectionhttpstoragedepssecretslogging
Environment
.envfiles that appear commit-eligible because they are not obviously
ignoredAPP_DEBUG=truein production-like environment files- production environment filename and
APP_ENVmismatches - weak, placeholder, or trivially short
APP_KEYvalues - hardcoded database credentials in Laravel config files
APP_URLusing HTTP in production-like environmentsSESSION_SECURE_COOKIE=falsein production-like environments- missing
security.txtfile atpublic/.well-known/security.txt
Authentication
- sensitive-looking routes without visible auth middleware
- sensitive routes with auth but no obvious policy, gate, role, or permission check
- models present without visible Gate or Policy definitions
- weak password hashing usage such as
md5()orsha1() - remember-me token usage without obvious expiry controls
- password handling without visible modern hashing usage
- impersonation-style features that need strong gating and auditability
- role or permission assignment driven directly from request input
- controllers using request input without visible validation
Injection
- raw SQL calls with interpolated variables
- models missing visible
$fillableor$guardeddeclarations - request input passed directly into query builder clauses
eval()usage- command execution sinks such as
exec(),shell_exec(), orproc_open() unserialize()usage- dynamic
include/requirepaths - unescaped Blade output (
{!! !!}) that can introduce XSS - XML parsing without entity loading protection (XXE)
- insecure random functions (
rand(),mt_rand(),array_rand(),shuffle()) - redirect targets built from user input (open redirects)
HTTP and Session Security
- CSRF middleware exceptions without visible justification
- insecure or missing session cookie flags
- wildcard CORS origins in config
- hardcoded non-localhost
http://URLs in config or routes - wildcard trusted proxy configuration in
TrustProxies - outbound HTTP or file fetches built from user-controlled URLs
- cloud metadata endpoint references that deserve SSRF review
- debug tooling routes for Telescope, Horizon, Debugbar, or Ignition that may be exposed
- auth routes (login, register, password reset) missing rate limiting
- missing security headers middleware (X-Content-Type-Options, X-Frame-Options, CSP)
- missing HSTS (
Strict-Transport-Security) configuration - external scripts and stylesheets loaded without Subresource Integrity (SRI)
Storage and Upload Handling
- file access paths influenced by request input
- file upload handling without visible validation
- public storage disk exposure
- files already present under
storage/app/public/ - user-controlled filenames passed into storage helpers
- archive extraction calls that deserve zip-slip review
- image processing code that lacks visible upload validation
Dependencies
- severely outdated Laravel core versions in
composer.lockorcomposer.json - known vulnerable Packagist packages via OSV lookup
- abandoned Composer packages
- PHP version constraints that may still permit unsupported runtimes
- missing
composer.lockas a reproducibility and auditability gap - best-effort notice when the vulnerability database is unreachable
Secrets
- hardcoded secret-like values in
.phpand.envfiles - committed private keys or certificate material
- URLs with embedded credentials
- cloud access key-like literals
- custom secret regex patterns from config
Logging
- debug mode enabled in environment files
- debug or trace log level in production-like environments
- passwords, tokens, secrets, or auth headers being logged
- leftover
dd(),dump(),var_dump(), orprint_r()calls - missing visible authentication failure logging
Installation
Prerequisites
- a Laravel project to scan
Install via Cargo
If you have a Rust toolchain with Cargo installed, you can install lsec directly:
cargo install lsec
The crate is also available on crates.io.
Download Binary
Pre-built binaries for Linux, macOS, and Windows are available on the
GitHub Releases page.
Build From Source
git clone https://github.com/AfaanBilal/lsec
cd lsec
cargo build --release
The resulting binary will be available at:
target/release/lsec
Run Without Installing
cargo run -- scan /path/to/laravel-app
CLI Usage
Scan a Project
lsec scan /path/to/laravel-app
The supplied path must be the Laravel application root. lsec validates this up front and errors out if the directory does not look like a Laravel project root.
List Supported Rules
lsec rules
Command Overview
lsec scan <path> [--only <categories>] [--skip <categories>]
[--only-rule <ids>] [--skip-rule <ids>]
[--format <pretty|json|sarif>] [--output <file>]
[--summary] [--quiet] [--ci]
[--fail-on <critical|high|medium|low|info>]
[--config <path>] [--baseline <path>] [--write-baseline]
[--min-confidence <float>]
lsec rules
lsec baseline write <path> [--baseline <file>] [--config <file>]
lsec baseline prune <path> [--baseline <file>] [--config <file>]
Common Examples
Pretty Terminal Report
lsec scan .
JSON Output
lsec scan . --format json
SARIF Output
lsec scan . --format sarif --output report.sarif
Show Summary Only
lsec scan . --summary
Scan Only Selected Categories
lsec scan . --only env,secrets,deps
Scan Only Specific Rules
lsec scan . --only-rule http.ssrf-user-url,secrets.private-key
Skip Specific Categories
lsec scan . --skip logging,http
Skip Specific Rules
lsec scan . --skip-rule logging.debug-artifact,auth.impersonation-feature
Write a Baseline File During Scan
lsec scan . --write-baseline
Write a Baseline File Explicitly
lsec baseline write .
Baseline commands use the same Laravel-root validation as scan.
Prune Stale Baseline Entries
lsec baseline prune .
Ignore Low-Confidence Findings
lsec scan . --min-confidence 0.8
Use an Existing Baseline File
lsec scan . --baseline ci/lsec-baseline.json
Use in CI and Fail on Medium or Higher
lsec scan . --ci --fail-on medium
Use a Custom Config File
lsec scan . --config ci/lsec.toml
Exit Codes
0: scan completed successfully and did not cross the configured CI failure
threshold1:--ciwas enabled and at least one finding met or exceeded the
configured failure severity2: CLI or runtime error
Severity ordering used by CI failure evaluation is:
CRITICAL, HIGH, MEDIUM, LOW, INFO
With --ci, --fail-on high fails the scan if any CRITICAL or HIGH
finding is present.
Configuration
By default, lsec looks for lsec.toml in the scan root. You can also point
to a file explicitly with --config.
Example lsec.toml
[scan]
exclude_paths = ["vendor/", "tests/", "node_modules/", ".git/", "storage/logs/"]
fail_on = "high"
min_confidence = 0.7
[rules]
min_confidence_overrides = { "auth.missing-route-authorization" = 0.8, "logging.auth-failure-missing" = 0.55 }
skip = ["logging"]
skip_ids = ["logging.debug-artifact"]
custom_secrets_patterns = [
"(?i)internal_token\\s*=\\s*['\\\"][A-Za-z0-9_-]{20,}['\\\"]",
"(?i)acme_live_[A-Za-z0-9]{24,}"
]
Supported Config Keys
[scan]
exclude_paths: path prefixes excluded from the project walkfail_on: default CI failure threshold when--ciis usedmin_confidence: global confidence floor applied before reporting
Default excluded paths are:
["vendor/", "tests/", "node_modules/", ".git/"]
[rules]
skip: categories to suppressskip_ids: exact rule ids to suppresscustom_secrets_patterns: additional regex patterns for secret detectionmin_confidence_overrides: per-rule confidence floors keyed by exact rule id
Output Formats
Pretty
The default terminal output prints each finding in a structured two-column block with severity badges, confidence, location, explanation, remediation, and snippet context. It starts and ends with tabular severity and category summaries.
JSON
JSON output includes:
- scan root
- whether summary-only mode was used
- counts by severity
- counts by category
- findings array with remediation and confidence
This format is useful for custom automation and post-processing.
SARIF
SARIF output is suitable for security tooling integrations that accept SARIF
2.1.0. lsec emits:
- tool metadata
- deduplicated rule entries for reported findings
- remediation help text per rule
- result items with severity mapping, confidence, and source locations
Current SARIF level mapping:
CRITICALandHIGH->errorMEDIUM->warningLOWandINFO->note
Rule Categories
You can scope scans with category and rule filters.
Category filters:
env, auth, injection, http, storage, deps, secrets, logging
Rule filters use exact ids, for example:
http.ssrf-user-url, secrets.private-key, deps.known-vuln
lsec rules also shows the default severity, default confidence, and a short remediation hint for each rule.
How Scanning Works
lsec first validates that the supplied path looks like a Laravel application root, then recursively walks the target repository, reads text files into memory, and skips:
- excluded path prefixes from config
- binary files
- invalid UTF-8 files
Checks are heuristic and static. They do not execute the application.
CI Integration
lsec is intended to be easy to wire into CI jobs.
Example
lsec scan . \
--ci \
--fail-on high \
--min-confidence 0.7 \
--baseline ci/lsec-baseline.json \
--format sarif \
--output lsec.sarif
Typical CI flow:
- run
lsecon the checked-out Laravel project - optionally load or refresh a baseline file for known legacy findings
- archive or upload
lsec.sarif - fail the job when findings meet the chosen severity threshold and confidence floor
GitHub Action
The official lsec GitHub Action
(source) installs lsec,
runs a scan, uploads SARIF to GitHub Code Scanning, and posts a sticky
summary comment on pull requests.
# .github/workflows/lsec.yml
name: lsec
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
security-events: write # required for SARIF upload
pull-requests: write # required for the PR summary comment
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: AfaanBilal/lsec-action@v1
See the action README
for inputs (fail-on, min-confidence, baseline, only, skip, …)
and outputs (result, findings-count, per-severity counts, …).
Public repositories can use GitHub Code Scanning for free. Private
repositories require GitHub Advanced Security.
Manual workflow
If you would rather not use the action, this minimal workflow does the
same SARIF upload by hand:
# .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
Current Limitations
lsec is deliberately lightweight, so it currently has a few important
limitations:
- checks are heuristic and may produce false positives or false negatives
- dependency vulnerability lookup is best-effort and depends on network access
- some findings are inferred from framework conventions rather than full code
flow analysis - secrets detection is regex-based
- route and auth analysis is intentionally shallow and favors speed over deep
semantic understanding - the target path must be the Laravel application root rather than an arbitrary subdirectory
- only Laravel-relevant patterns currently implemented in the source are
checked
Roadmap Ideas
Potential future improvements include:
- richer Laravel version support awareness
- more framework-aware route and middleware analysis
- richer baseline lifecycle support
- diff-only scanning for pull requests
- autofix or remediation suggestions
Development
Build
cargo build
Run Tests
cargo test
Run Against This Repo
cargo run -- scan .
List Rules During Development
cargo run -- rules
Project Structure
src/
main.rs CLI entrypoint
config.rs configuration loading
models.rs shared scan models and enums
scanner/ repository loading and file discovery
rules/ security rule implementations
reporter/ pretty, JSON, and SARIF renderers
Security Review Guidance
lsec is a developer aid, not a replacement for a full security review. Treat
findings as prompts for investigation, especially for high- and critical-level
results. A clean scan does not guarantee a secure application.
Contributing
All contributions are welcome. Please create an issue first for any feature request
or bug.
License
lsec is released under the MIT License.
Check out the full license here.
Copyright (c) 2026 Afaan Bilal https://afaan.dev
License
MIT License
Copyright (c) 2026 Afaan Bilal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.