PHPackages                             laramint/php-security-scanner - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. laramint/php-security-scanner

ActiveLibrary[Security](/categories/security)

laramint/php-security-scanner
=============================

Framework-agnostic static security scanner for PHP. Detects SQLi, XSS, command injection, path traversal, insecure deserialization, weak crypto, hardcoded secrets, and more.

v0.1.0(3w ago)0380↓50%1MITPHPPHP ^8.0CI passing

Since May 15Pushed 3w agoCompare

[ Source](https://github.com/laramint/php-security-scanner)[ Packagist](https://packagist.org/packages/laramint/php-security-scanner)[ RSS](/packages/laramint-php-security-scanner/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (7)Versions (2)Used By (1)

 [![Laravel Brain](art/php-security-scanner-logo.png)](art/php-security-scanner-logo.png)

 **A framework-agnostic, static security scanner for PHP.**

 [![PHP](https://camo.githubusercontent.com/119ab04a0e7c293f3d9a4648c84e373315e69d1f6d6aa8f2065519dade71686b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e302532422d3737374242343f7374796c653d666c61742d737175617265266c6f676f3d706870)](https://camo.githubusercontent.com/119ab04a0e7c293f3d9a4648c84e373315e69d1f6d6aa8f2065519dade71686b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e302532422d3737374242343f7374796c653d666c61742d737175617265266c6f676f3d706870) [![License](https://camo.githubusercontent.com/422db9fd40f5831c765cf6530b6750c081b696bd18d904cf89554df98c676277/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e3f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/422db9fd40f5831c765cf6530b6750c081b696bd18d904cf89554df98c676277/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e3f7374796c653d666c61742d737175617265) [![Sponsor](https://camo.githubusercontent.com/3e47708dc2aa829eb49a90d2f92907ff3d54743907debef18ac1061bb02a082b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53706f6e736f722d4769744875622d4541344141413f7374796c653d666c61742d737175617265266c6f676f3d6769746875622d73706f6e736f7273)](https://camo.githubusercontent.com/3e47708dc2aa829eb49a90d2f92907ff3d54743907debef18ac1061bb02a082b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53706f6e736f722d4769744875622d4541344141413f7374796c653d666c61742d737175617265266c6f676f3d6769746875622d73706f6e736f7273) [![Buy Me a Coffee](https://camo.githubusercontent.com/fbc0d58eeb26312165d87448dbc704659f68c6b9fee054c24177388b17f5b3a9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4275792532304d6525323061253230436f666665652d4d724d617263684f6e652d4646444430303f7374796c653d666c61742d737175617265266c6f676f3d6275792d6d652d612d636f66666565266c6f676f436f6c6f723d626c61636b)](https://www.buymeacoffee.com/MrMarchOne)

PHP Security Scanner
====================

[](#php-security-scanner)

A framework-agnostic, static security scanner for PHP. Detects the OWASP-ish core of vulnerabilities — SQL injection, XSS, command injection, path traversal, insecure deserialization, weak crypto, hardcoded secrets, weak randomness, `eval`, disabled TLS verification, XXE, open redirect, insecure cookies — using AST analysis with kind-aware taint tracking. Also audits `composer.lock` against the **Packagist Security Advisories** database so known-CVE dependencies fail the build.

Ships as a CLI binary, a library you can embed, a PHAR you can download, and a GitHub Action that uploads SARIF to Code Scanning.

Install
-------

[](#install)

### As a dev dependency

[](#as-a-dev-dependency)

```
composer require --dev laramint/php-security-scanner
./vendor/bin/php-security-scanner scan src/
```

### As a PHAR (no Composer conflicts)

[](#as-a-phar-no-composer-conflicts)

```
curl -fsSL https://github.com/laramint/php-security-scanner/releases/latest/download/php-security-scanner.phar \
    -o /usr/local/bin/php-security-scanner
chmod +x /usr/local/bin/php-security-scanner
php-security-scanner scan src/
```

Usage
-----

[](#usage)

```
php-security-scanner scan [paths...] [options]

  --format=pretty|json|sarif|junit          (default: pretty)
  --severity-threshold=low|medium|high|critical  (suppress below)
  --fail-on=critical|high|medium|low|none   (CI gate; default: high)
  --config=PATH                             (.yaml, .yml, or .php; auto-discovers
                                             php-security-scanner.yaml/.yml/.php)
  --exclude=GLOB                            (repeatable)
  --rule=ID                                 (run only these; repeatable)
  --skip-rule=ID                            (skip these; repeatable)
  --baseline=baseline.json
  --update-baseline
  --output=PATH
  --extension=Fqcn\\To\\Extension           (repeatable)
  --no-progress
  --no-audit                                (skip composer.lock CVE audit)
  --audit-offline                           (use cached advisories only)
  --explain                                 (emit INFO notes for sinks the
                                             scanner considered but kept silent,
                                             naming the sanitizer/cast/method
                                             that cleared the taint)

php-security-scanner list-rules
```

Exit codes: `0` clean • `1` finding ≥ `--fail-on` • `2` config error.

The pretty progress output is PHPUnit-style — one character per file written to **stderr** so piping JSON/SARIF/JUnit to stdout stays clean:

```
....⨯.....⨯..............................................   60 / 134 (44%)
.....................................⨯......                134 / 134 (100%)

  FAIL  134 file(s) scanned in 1.42s, 6 finding(s).

```

`.` (gray) = clean file, `⨯` (red bold) = file with at least one finding. Trailing label shows progress (`done / total (%)`) and the final summary shows the elapsed scan time.

By default, the file walker silently skips `vendor/`, `node_modules/`, `bower_components/`, `.git/`, `storage/`, `bootstrap/cache/`, `build/`, `dist/`, `out/`, `coverage/`, IDE state (`.idea/`, `.vscode/`, `.fleet/`, `.zed/`), and tool caches (`.phpunit.cache/`, `.phpstan.cache/`, `.pint.cache/`, `.psalm.cache/`, `.rector.cache/`, etc.) — see [`FileFinder::DEFAULT_SKIP_DIRS`](src/Discovery/FileFinder.php). Only add project-specific extras to `exclude`.

Rules
-----

[](#rules)

IDDefault severityDetects`eval`high`eval()` use, especially with tainted input`sql.injection`criticalTainted input concatenated into raw SQL`cmd.injection`criticalTainted input passed to `exec`/`shell_exec`/`system`/...`path.traversal`highTainted input used as file path or `include`/`require` target`xss.echo`highTainted input echoed without `htmlspecialchars``deserialize`critical`unserialize()` on tainted input`crypto.weak-hash`medium`md5`/`sha1` on security-sensitive data`crypto.deprecated`high`mcrypt_*` functions`random.insecure`medium`rand`/`mt_rand`/`uniqid` used for tokens/secrets/CSRF`secret.hardcoded`highHigh-entropy literals in `$api_key`, `SECRET`, etc.`ssl.disabled`critical`CURLOPT_SSL_VERIFYPEER`/`VERIFYHOST` set to false/0`xxe`highXML parsed without `LIBXML_NONET``redirect.open`medium`header("Location: …")` from tainted input`cookie.insecure`medium`setcookie()` without `secure`/`httponly`/`samesite``assert.use`high`assert()` with a string/tainted argument (eval-like)`cmd.backticks`highBacktick shell-exec operator, especially with tainted input`eval.mb-ereg`high`mb_ereg_replace()` with the `e` (eval) modifier`file.inclusion`criticalLFI/RFI: tainted input in `include`/`require``callable.tainted`criticalTainted value used as a callable in `call_user_func` and friends`object.tainted-new`highTainted value used as class name in `new $cls()``ssrf`highTainted URL passed to `curl_init`, `curl_setopt(CURLOPT_URL)`, Guzzle, etc.`cors.permissive`high`Access-Control-Allow-Origin: *` or reflected origin`net.ftp-cleartext`medium`ftp_connect()` — credentials/data in clear (use `ftp_ssl_connect`)`ldap.anonymous-bind`high`ldap_bind()` with empty/null/missing password`crypto.mcrypt-deprecated`highUse of any `mcrypt_*` function`crypto.md5-loose-eq`mediumLoose `==`/`!=` comparison on `md5`/`sha1`/`hash` output`crypto.md5-password`high`$password = md5(...)` / `sha1(...)` — use `password_hash()``crypto.cbc-static-iv`high`openssl_encrypt` in CBC mode with a constant/empty IV`crypto.openssl-decrypt-novalidate`medium`openssl_decrypt` with unauthenticated mode (CBC/ECB)`crypto.sha224`low`hash('sha224', …)` — truncated SHA-256, prefer sha256/sha512`info.phpinfo`high`phpinfo()` exposes runtime config and environment`audit.composer.vulnerable-package`from advisory (default high)Composer dependency with a known CVE published on Packagist`audit.composer.network-error`infoCould not contact the Packagist advisories endpoint while auditing `composer.lock`Run `php-security-scanner list-rules` to see the live list.

Dependency CVE audit
--------------------

[](#dependency-cve-audit)

Whenever the scanner finds a `composer.lock` at the root of any scanned path, it queries the [Packagist Security Advisories API](https://packagist.org/api/security-advisories/) for the exact installed versions and emits one finding per (package, advisory) pair — same `Finding` shape as code rules, so baselines, severity overrides, `--fail-on`, and every reporter format work uniformly. CVE id, advisory link, affected version range, and report date are stored under each finding's `metadata`.

- Auto-discovered — no flag required.
- Results are cached at `sys_get_temp_dir()/php-security-scanner-advisories.json` for 6 hours.
- `--no-audit` disables the audit entirely. `--audit-offline` reuses the warm cache and never contacts Packagist (useful in CI without egress).
- A transient network failure produces a single informational `audit.composer.network-error` finding (severity `info`) so it doesn't trip `--fail-on high`.
- Constraint matching uses `composer/semver` when present on the autoloader; otherwise a built-in matcher covers the operators Packagist actually emits (`>=`, ``, `query("... " . $id);`), the note isn't emitted — at the sink the engine only sees a clean `$id` variable.

### Limitations

[](#limitations)

Intra-procedural only — no cross-file or cross-function data flow, no class-property taint, no array-key precision. Combined with the zero-FP policy above, this means the scanner will miss any vulnerability whose dangerous flow crosses function/file boundaries or passes through a sanitizer the engine doesn't recognize. The scanner is fast and predictable; for deeper analysis pair it with [Psalm](https://psalm.dev) or [PHPStan](https://phpstan.org) with taint plugins.

Development
-----------

[](#development)

```
git clone https://github.com/laramint/php-security-scanner.git
cd php-security-scanner
composer install
composer test          # PHPUnit
composer test:types    # PHPStan
```

Smoke-scan the bundled fixtures:

```
./bin/php-security-scanner scan tests/fixtures/vulnerable --fail-on=none
./bin/php-security-scanner scan tests/fixtures/safe         # must be clean
```

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

php-security-scanner
====================

[](#php-security-scanner-1)

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance94

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity28

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

26d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/23707114?v=4)[Abdelrahman Muhammed](/maintainers/mrmarchone)[@mrmarchone](https://github.com/mrmarchone)

---

Top Contributors

[![mrmarchone](https://avatars.githubusercontent.com/u/23707114?v=4)](https://github.com/mrmarchone "mrmarchone (17 commits)")

---

Tags

phpstatic analysissecurityowaspscannervulnerabilitySAST

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/laramint-php-security-scanner/health.svg)

```
[![Health](https://phpackages.com/badges/laramint-php-security-scanner/health.svg)](https://phpackages.com/packages/laramint-php-security-scanner)
```

###  Alternatives

[friendsoftypo3/content-blocks

TYPO3 CMS Content Blocks - Content Types API | Define reusable components via YAML

101466.4k44](/packages/friendsoftypo3-content-blocks)[aeliot/todo-registrar

Register TODOs from source code in issue tracker

153.0k](/packages/aeliot-todo-registrar)[jolicode/castor

A lightweight and modern task runner. Automate everything. In PHP.

54642.4k4](/packages/jolicode-castor)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
