I built an offline CLI that audits a legacy PHP app in one command — and shows you the fix

작성자

카테고리:

← 피드로
DEV Community · getobserver · 2026-07-10 개발(SW)
Cover image for I built an offline CLI that audits a legacy PHP app in one command — and shows you the fix

getobserver

Every time I inherit a legacy PHP app — a CodeIgniter admin panel, an old Laravel service, a WordPress site someone’s cousin built — day one is the same ritual: grep for password, squint at config.php, wonder if display_errors is on in prod, and hope there isn’t a hardcoded API key waiting to ruin my week.

So I built a tool to do that first pass for me, in one command, offline. It’s called Observer, it’s a single Go binary, and it’s free and MIT.

observer analyze ./your-app --out report.html

Enter fullscreen mode Exit fullscreen mode

Point it at a folder, get one self-contained HTML report. No server, no account, no instrumentation, nothing sent anywhere.

What it actually found

I ran it against a real CodeIgniter 3 admin app I’d picked up. A few seconds later:

Project:   admin-panel
Language:  PHP
Framework: CodeIgniter 3 [High]
Database:  MySQL [High]

Security score: 81/100 (B)   Code health: 100/100 (A)
Static analysis: issues across secrets, config & dependencies

  [High]  application/config/firebase.php   Hardcoded Google API key
  [High]  index.php                         display_errors enabled
  [Med ]  composer.json                     End-of-life PHP version

Enter fullscreen mode Exit fullscreen mode

None of these are exotic. That’s the point — the boring, high-impact stuff is exactly what gets missed on a handover, and it’s what an attacker finds first. A hardcoded key in a config file that’s sitting in the repo is a bad afternoon waiting to happen.

It doesn’t just flag — it shows the fix

This is the part I care about most. Most free scanners hand you a wall of findings and leave you to Google each one. Every Observer finding carries a concrete before → after fix, right in the report — no AI key required, fully offline:

// Before
$db->query("SELECT * FROM users WHERE id = " . $id);

// After
$db->query("SELECT * FROM users WHERE id = ?", [$id]);

Enter fullscreen mode Exit fullscreen mode

Each finding also gets a severity, a file:line, and a CWE/OWASP tag, so you can triage instead of just staring.

What it checks

  • Security rules across PHP, JS/TS, Python, Java & Ruby — hardcoded secrets, SQL injection, XSS, command execution, insecure deserialization, weak crypto, dangerous config
  • Dependency CVEs via OSV.dev (--cve) — Composer, npm, PyPI, Go
  • A Security score and a Code-Health score (A–F) plus an estimated fix effort
  • SARIF / JSON / CSV output + a GitHub Action, so it drops into CI
  • It also auto-detects Semgrep / PHPStan / Bandit / gosec if you already have them, and folds their findings into the same report

Offline by default (and provably so)

No telemetry, no phone-home. The only things that touch the network are explicitly opt-in (--cve, or --ai with your own OpenAI key). For regulated or client-confidential work there’s a flag that makes it enforceable:

observer analyze ./client-app --assert-offline
# Offline mode: no network I/O.

Enter fullscreen mode Exit fullscreen mode

It refuses any network-touching option and keeps the AI on a local heuristic. Handy when you’re auditing someone else’s code under NDA and the code genuinely cannot leave the machine.

What it’s not

It’s not trying to replace SonarQube, Snyk, or Sentry. Those go deep in one dimension and want a server, a cloud account, or instrumentation. Observer is the opposite shape: a zero-setup, offline snapshot for the moments those are too heavy — a legacy handover, a quick client audit, an air-gapped scan, or just “what’s wrong with this thing I just cloned?” Run both; they answer different questions.

Try it / tell me what it misses

It’s genuinely free and MIT — grab a binary and point it at any repo:

👉 https://github.com/sanks205/getobserver

There are a few optional one-time paid add-ons (branded PDF reports, scheduled scans, deeper framework rule packs), but the whole core scanner is free with no account.

This is early, and I’d really value feedback from people who work on older codebases: what did it flag that was noise? What rule would’ve actually saved you an afternoon? Drop a comment — that’s what I’m building from next.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다