Skip to content

ShaikhNaasir/php-scanner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PHP Danger Scanner

A static-analysis tool that flags dangerous PHP patterns — RCE, command/SQL injection, LFI/RFI, insecure deserialization, hardcoded secrets, and best-practice issues — in a single file, a whole folder, or an entire .zip project. Every finding shows the exact file, line, and column, the offending code highlighted, and how to fix it.

It runs two ways from the same detection engine (scanner-core.js):

  • Browser only — open index.html. Everything (including ZIP extraction) happens locally in your browser. No server, no upload, works offline.
  • With the backend — run npm start and open the printed URL. Scanning happens on a small Node server so the UI stays responsive on large projects. The page detects the backend automatically and shows a Server / Local badge you can toggle.

Table of contents


Quick start

Option A — no install, no server (offline)

Just open the file in any modern browser:

double-click  index.html

Keep index.html and scanner-core.js in the same folder — the page loads the engine from that file. The badge in the top-right will read Local.

Option B — run the backend (recommended for big projects)

Requires Node 18+ (no packages to install — the server uses only built-in modules):

cd php-scanner
npm start
#   PHP Danger Scanner · core v2.0.0 · 73 rules
#   Running at  http://127.0.0.1:3000

Open http://127.0.0.1:3000. The badge will read Server.


What it detects

12 categories, 73 rules, graded into four severities:

Severity Meaning Example patterns
🔴 Critical Almost certainly exploitable / a backdoor eval($_POST[...]), system(...), dynamic include, unserialize($_COOKIE), AWS keys, private keys
🟠 High Serious risk, needs review shell_exec, deprecated mysql_*, arbitrary file write/delete, weak md5() password hashing, hardcoded credentials
🟡 Medium Risky depending on context permissive chmod 0777, open redirect, strcmp() auth, error_reporting(0), phpinfo()
🔵 Low Best-practice / code-quality smells var_dump() left in, $_REQUEST, loose ==, short open tags, TODO/FIXME, goto

Categories: Remote Code Execution, Command Injection, SQL Injection, XSS, File Inclusion, Dangerous File Ops, Deserialization, Upload, Auth/Session, Hardcoded Secrets, Best Practices, Misc Dangerous.

The scanner is comment-aware (string-safe): dangerous-looking code inside //, #, or /* ... */ comments is ignored, which greatly reduces false positives.


How to use — step by step

Step 1 — Add your code

The left panel gives you four ways to load code. Use whichever fits:

  1. Whole .zip — drag a .zip onto the drop zone (or click Files / .zip). Every PHP-like file inside is extracted and scanned automatically. Best for a whole project.
  2. Whole folder — click Whole Folder and pick a directory; the full tree is loaded.
  3. Individual files — select one or more .php, .phtml, .inc, .module, etc.
  4. Paste code — paste a snippet directly into the “Or Paste Code Directly” box.

Scannable extensions: .php .phtml .inc .module .php5 .php7 .phps .php3 .php4 .ctp .tpl. Loaded files appear in a list with sizes and a Clear all button.

Step 2 — Choose categories & options

  • Scan Categories — toggle which vulnerability classes you care about. (You can also change these after a scan — see live filtering.)
  • Scan Options
    • Ignore comments (on) — skip matches inside comments.
    • Skip vendor / node_modules (on) — ignore vendor/, node_modules/, .git/, etc.
    • Scan on server (shown only when the backend is running) — run the scan on the server instead of in the browser.

Step 3 — Run the scan

Click Start Security Scan. A progress bar shows files scanned / percentage. Large projects stay responsive (the browser scan yields on a time budget; the server scan runs off the UI thread entirely).

Step 4 — Read the results

The results panel is organised top-to-bottom:

  • Verdict banner — a one-line takeaway (red if any critical/high issues exist).
  • Severity stats — counts of Critical / High / Medium / Low.
  • Most dangerous files — files ranked by a weighted risk score. Click a file to jump to and filter its findings. This is the fastest way to "find the dangerous file".
  • Findings list — grouped by file. Each card shows the title, line & column, and a severity tag. Click a card to expand:
    • a plain-language description of the risk,
    • the code context with the exact match highlighted,
    • a Remediation box telling you how to fix it,
    • the category.

Step 5 — Filter, search, and focus

  • Severity filter — show only Critical / High / Medium / Low (or All).
  • Search box — type to filter findings by title, file path, or matched code.
  • Click a file in the overview to see only that file’s findings (Clear file filter to reset).
  • Live category toggles — turning a Scan Category off/on in the left panel updates the results instantly, with no re-scan.
  • Expand / Collapse — open or close all finding cards at once.

Step 6 — Export a report

Click Export Report (top-right) and choose a format:

Format Use it for
HTML A shareable, self-contained report you can open in any browser or print to PDF.
JSON Feeding results into other tools / CI.
CSV Opening in Excel / Google Sheets.
TXT A quick plain-text summary.

Re-scan hint: options that change what is detected (Ignore comments, Skip vendor, editing pasted code, adding/removing files) can’t update live. When you change one after a scan, the button turns into “Re-scan to apply changes.” Category toggles, search, and severity filters always update instantly.


Try it now with the sample files

Two ready-made fixtures are included so you can confirm everything works:

  • test.php — deliberately vulnerable, covering every category. It also contains a docblock with eval(...)/system(...) inside a comment to prove comment-awareness.
  • test2.php — a mix of safe code (a prepared PDO statement, password_hash, htmlspecialchars) and real problems, to show it flags issues without over-flagging good code.

How to test: open the app, click Files / .zip (or drag the files in), select test.php and test2.php, then Start Security Scan.

Expected results (with default options):

File Findings Critical High Medium Low
test.php 42 15 10 9 8
test2.php 15 1 4 2 8

You should also see that the eval/system inside the comment block in test.php are not reported, and that test2.php’s prepared PDO query is not flagged as SQL injection. (Exact numbers may shift slightly as rules evolve.)

You can also verify from the command line without the UI:

node -e "const C=require('./scanner-core.js'),fs=require('fs');\
for(const f of ['test.php','test2.php']){\
const {findings}=C.scanAll([{name:f,content:fs.readFileSync(f,'utf8')}],{skipComments:true});\
console.log(f, findings.length, 'findings');}"

Local vs Server mode

Local (badge: 💻) Server (badge: 🖥️)
How Open index.html directly npm start, open the URL
Where scanning runs In your browser On the Node backend
Network None — fully offline localhost only
Best for Quick checks, no setup Large projects / many files

The page auto-detects the backend on load. If the server is running you’ll get a Scan on server toggle (on by default); if it isn’t reachable, the app silently uses Local mode. If a server scan ever fails mid-run, it automatically falls back to a local scan.


Backend API

All responses are JSON. Base URL defaults to http://127.0.0.1:3000.

Method & path Body Returns
GET /api/health { ok, version, categories, rules }
POST /api/scan { "files": [{ "name", "content" }], "options": { "skipComments" } } scan result
POST /api/scan/zip raw .zip bytes (Content-Type: application/zip) scan result

A scan result. Each finding nests its rule (severity, category, title, description, remediation) and carries the surrounding context lines so a report can show the code:

{
  "ok": true,
  "engine": "server",
  "filesScanned": 2,
  "filesAffected": 2,
  "totalFindings": 57,
  "capped": false,
  "findings": [
    {
      "file": "test.php",
      "line": 16,
      "col": 1,
      "match": "eval(",
      "matchLine": 2,
      "matchIndex": 0,
      "matchLen": 5,
      "context": [
        { "lineNum": 14, "text": "// ---- Remote Code Execution ----" },
        { "lineNum": 16, "text": "eval($_POST['code']);" }
      ],
      "rule": {
        "severity": "critical",
        "category": "rce",
        "title": "eval() — Direct Code Execution",
        "description": "eval() executes arbitrary PHP code…",
        "remediation": "Never use eval()…"
      }
    }
  ]
}

Read a finding's severity as finding.rule.severity and its location as finding.file / finding.line / finding.col.

Examples:

# Health
curl -s http://127.0.0.1:3000/api/health

# Scan a zipped project and print the finding count
curl -s -X POST http://127.0.0.1:3000/api/scan/zip \
  -H 'Content-Type: application/zip' --data-binary @project.zip | jq .totalFindings

# Scan a single file's contents as JSON
curl -s -X POST http://127.0.0.1:3000/api/scan \
  -H 'Content-Type: application/json' \
  -d "{\"files\":[{\"name\":\"a.php\",\"content\":\"<?php eval(\$_POST[1]);\"}]}" | jq .totalFindings

Testing

npm test

Runs 31 checks across three areas: the shared rule engine (rule integrity, detection, comment handling, sorting), the Node ZIP extractor, and a live end-to-end pass that boots the server and exercises /api/health, /api/scan, /api/scan/zip, static hosting, and error handling.


Configuration

Environment variables for the server:

Variable Default Purpose
PORT 3000 Port to listen on
HOST 127.0.0.1 Interface to bind
PORT=8080 HOST=0.0.0.0 npm start

Built-in safety limits: request body cap 120 MB, per-file cap 3 MB, and a scan cap of 20,000 findings for pathological inputs.


Troubleshooting

  • Badge stuck on “Local” while the server is running. Make sure you opened the app via the server URL (http://127.0.0.1:3000) and not by double-clicking the file — a page opened from file:// cannot reach the backend, so it stays Local by design.
  • “Could not load scanner-core.js.” scanner-core.js must sit next to index.html.
  • ZIP won’t open / “ZIP64 not supported.” Very large or encrypted archives aren’t supported; re-zip without encryption, or upload the folder instead.
  • A big file was skipped. Individual files over 3 MB are skipped by design.
  • Port already in use. Start with a different port: PORT=8080 npm start.

Limitations & disclaimer

This is a pattern-based scanner: fast and broad, but it does not perform full data-flow analysis, so it can produce false positives and miss context-specific bugs. Treat findings as leads, not proof, and always pair it with manual review. The bundled test.php / test2.php contain intentionally dangerous code for demonstration only — never deploy them.


Project layout

File Purpose
index.html Frontend UI (loads the shared engine).
scanner-core.js Shared detection rules + scan engine (UMD: browser and Node).
server.js Zero-dependency Node HTTP server: static hosting + scan API.
lib/unzip.js Server-side ZIP extraction (built-in zlib).
test/run-tests.mjs Test suite (core + unzip + live server).
test.php, test2.php Sample vulnerable files for a quick demo.

The browser and the server share one rule set in scanner-core.js, so results are identical no matter where a scan runs.

License

MIT — see LICENSE.

About

A client-side PHP security scanner that detects dangerous patterns like RCE, SQLi, XSS, file inclusion, command injection, and 40+ vulnerability types across your entire project

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages