mjg/apps
Changelog

What changed, when.

Per-product release notes. Every tool and app gets its own section as it ships.

Larascan

9 releases
v0.5.4Apr 24, 2026SEC-011 — SQL-keyword strings with interpolation (scanner-miss caught during dogfood)
  • featureNew rule SEC-011 detects SQL-keyword string literals (SHOW/SELECT/CREATE/ALTER/DROP/INSERT/UPDATE/DELETE/TRUNCATE/RENAME) with variable interpolation — including outside DB::method() call sites, which SEC-003 specifically targets. Severity: high.
  • improvementDedupe logic ensures SEC-003 and SEC-011 never double-flag the same file:line. SEC-003 runs first and SEC-011 only fires at untouched locations.
  • noteDriven by a real dogfood run: Claude triaging Finagle caught an Eloquent accessor (getCreateSqlAttribute) that built a CREATE INDEX statement via string concatenation — same injection risk as SEC-003 but different code shape. SEC-011 now closes that gap.
  • noteRule count: 23 → 24. Added to all four profiles (default, pre-launch, rescue, monthly). Not auto-fixable — resolution requires human-written validator logic.
v0.2.0Apr 23, 2026Blade, more security, and fairer scoring
  • featureBLADE-001: Unescaped Blade output ({!! !!}) detection — flags the XSS risk that was completely missed in v0.1.
  • featureBLADE-002: State-changing <form> elements missing @csrf directive.
  • featureSEC-006: unserialize() call detection — classic PHP object injection vector.
  • featureSEC-007: eval() call detection — direct remote code execution risk.
  • featureSEC-008: APP_KEY empty, placeholder, or too short.
  • featureSEC-009: APP_ENV=local flagged as medium severity (prod safeguards skipped).
  • featureSEC-010: Session cookie http_only or secure hard-coded to false.
  • featurePERF-004: Model::all() detection — catches the classic memory bomb as tables grow.
  • featureSLOP-003: PHP classes in app/ without a namespace (usually hand-copied or AI-generated code that bypassed PSR-4).
  • featureDEPLOY-004: SESSION_DRIVER=file on multi-instance deploys.
  • improvementScoring now uses per-severity caps so a large repo can't auto-F from one noisy category. Critical can still sink a score fast; high/medium/low flatten after a threshold.
  • improvementHTML report polish: 'Top priority fixes' callout pulls the 3 highest-severity findings to the top, category chips added per finding.
  • improvementBlade file scanner: reports/views is walked for *.blade.php alongside PHP files.
  • note13 rules → 23 rules. Still zero Composer dependencies. Still PHP 8.1+ only.
v0.3.0Apr 23, 2026Auto-fix, trend tracking, and CI
  • feature--fix mode applies safe corrections for 6 rules automatically: SEC-001 (APP_DEBUG=true → false), SEC-005 (remove standalone dd()/dump()/var_dump()/print_r()), SEC-008 (generate fresh APP_KEY), SEC-009 (APP_ENV=local → production), SEC-010 (harden cookie flags), DEPLOY-003 (pin PHP version in composer.json).
  • feature--dry-run flag previews every fix without writing.
  • featureAuto-fix aborts on a dirty git working tree. --force bypasses the guard when you really mean it.
  • featureTrend tracking via --save-history: writes a summary to .larascan/history/ on every scan. Subsequent reports include score_previous and score_delta in meta.
  • featureGitHub Actions template (templates/github-workflows/larascan.yml) runs the scanner on every PR, posts a sticky bot comment with findings, and fails the build on grade F.
  • improvementScoring recalibrated: critical severity is uncapped (stacking crits keeps tanking the score), while high/medium/low keep soft caps so a noisy category can't auto-fail a big repo.
  • noteRisky categories (mass assignment, raw SQL interpolation, N+1, Blade XSS, missing @csrf, unserialize, eval) stay advisory by design — humans only.
v0.4.0Apr 23, 2026Rule profiles, diff mode, plugins, webhooks, interactive
  • featureFour named rule profiles: default (all rules, v0.3 scoring), pre-launch (stricter weights + wider caps for the moment before ship), rescue (security-weighted for taking over abandoned codebases — crits +25%, highs +50%), monthly (12 core rules for fast recurring health checks).
  • featureDiff mode via new scripts/diff.php — compare two JSON reports, get added/removed/unchanged findings plus score delta. Finding identity = (rule_id, file, line).
  • featureCustom rule plugins — drop PHP files into <repo>/.larascan/rules/ defining userRule_<IDENTIFIER> functions. Auto-discovered and invoked after built-in rules. USER-XXX rule IDs.
  • featurePlugin safety: files containing any top-level code outside function definitions are rejected with a warning to stderr. A 4-state validator lets through declare, namespace, use, and pure function definitions — but not classes, echoes, requires, or arbitrary statements.
  • featureWebhook support via --webhook=<URL>. Auto-detects Slack, Discord, or generic from the hostname (override with --webhook-type). Slack gets blocks, Discord gets severity-colored embeds, generic gets the full report JSON. Webhook failure never fails the scan.
  • featureInteractive walkthrough via --interactive. Step through findings one at a time with [f]ix / [s]kip / [o]pen / [q]uit. Requires a TTY — skipped cleanly in CI.
  • improvementJSON output gained meta.profile and meta.user_rule_count. All v0.3 keys preserved. render.php consumes v0.4 reports without modification.
  • noteStill zero Composer dependencies. Still PHP 8.1+ only. Backward-compatible with v0.3 — every v0.3 invocation produces the same output in v0.4 (just with new meta fields).
v0.5.0Apr 23, 2026Composer, baseline, config file, and SARIF
  • featureComposer package (mjgapp/larascan) — install with `composer require --dev mjgapp/larascan` and run via `vendor/bin/larascan`. Zero runtime dependencies beyond PHP 8.1+.
  • featureBaseline file — `.larascan/baseline.json` stores accepted findings. Create with `--update-baseline`; subsequent scans automatically filter them. Use `--ignore-baseline` for a full audit. Makes adoption on legacy codebases actually possible without every PR failing.
  • featureConfig file — `.larascan/config.json` (or `.larascan.json`) sets team-wide defaults for profile, excluded rules, excluded paths (glob), webhook URL+type, and brand. CLI flags override.
  • featureSARIF output — `--format=sarif` emits spec-compliant SARIF 2.1.0. Uploadable via github/codeql-action/upload-sarif so findings appear as native PR annotations and repo-level security alerts in GitHub.
  • featureSeverity level mapping for SARIF: critical/high → error, medium → warning, low → note.
  • improvementBin shim at bin/larascan is a require-based wrapper — same entry point whether via Composer or direct script invocation.
  • noteJSON output gained meta.baseline (present/ignored_count/generated_at) and meta.config (when a config was loaded). All v0.4 keys preserved. Backward-compatible with v0.3+ pipelines.
v0.5.1Apr 23, 2026Calibration — from dogfooding 16 of my own Laravel apps
  • fixDropped ->increment() / ->decrement() from the N+1 detection pattern. They're counter writes, not lookups; lumping them in caused a confirmed false positive on real code (MailLore's RuleProcessorService).
  • fixBumped per-file N+1 cap from 1 → 3. The old 'one hit per file' cap masked a real nested N+1 (CleanupJob.php:72) because the scanner bailed after matching a false-positive outer loop first.
  • fix.gitignore-aware severity for bare .env: if .env is in .gitignore (typical on Laravel Cloud / Vercel / any platform that injects prod env vars), SEC-001 downgrades from critical → low, SEC-008 crit → low, SEC-009 medium → low — each with a prepended advisory note. .env.production / .env.prod / .env.staging keep full severity unconditionally.
  • featureBaseline entries now carry optional `reason` + `note` fields. When you run --update-baseline, the scanner preserves existing reason/note values by fingerprint, so your triage decisions survive regeneration.
  • featureMarkdown action-plan renderer gained a '🗂 Accepted (baselined)' section that surfaces baselined findings with their reason and note — so the judgment trail is visible, not just the count.
  • improvementDogfood impact across my own 16-repo Laravel portfolio: 15 of 16 repos jumped ~22 points, average delta +63 per repo. Same detection precision on real issues, dramatically less noise on local-dev config.
  • noteAll existing baseline files remain compatible — entries without reason/note load and filter correctly. This is a pure calibration release; no CLI surface changes.
v0.5.2Apr 23, 2026BLADE-001 context awareness — Chart.js, nested-e(), framework methods, config allowlist
  • fix--fix mode now SKIPS findings on gitignored .env files. The scanner already downgrades .env findings to low when .gitignore excludes them — the fixers now honor that same detection instead of rewriting a local-dev file to APP_ENV=production and breaking Herd.
  • improvementBLADE-001 is now <script>-context aware. {!! json_encode(...) !!} inside a <script>...</script> block is the Chart.js idiom — legitimate in every real Laravel dashboard app — and is now suppressed. On FinancesWiz alone, this drops 17 of 21 BLADE-001 findings.
  • improvementRecognizes {!! nl2br(e(...)) !!} and {!! Str::markdown(e(...)) !!} as safe-by-construction: the inner e() already HTML-escapes, and the outer nl2br/markdown only operates on the already-escaped string.
  • improvementFramework-method allowlist for known-safe no-arg calls: twoFactorQrCodeSvg, twoFactorQrCodeUrl, twoFactorSecret, toHtml, renderMarkdown. Jetstream/Fortify 2FA pages stop being flagged.
  • featureConfig-level blade_safe_helpers allowlist — add project-specific helpers like `markdown_to_html` to .larascan/config.json and they're treated as trusted. User input is validated and regex-quoted, so the config can't be weaponised to inject regex metacharacters.
  • noteSignal-to-noise across the 16-repo portfolio: average ~22-point score improvement, BLADE false-positive rate dropped from ~80% to nearly 0 on dashboard apps. Driven entirely by two real dogfood sessions (MailLore + FinancesWiz) — no speculative features.
v0.5.3Apr 23, 2026N+1 siblings context + baseline pruning
  • featurePERF-001 findings now include a siblings array — additional DB-call sites detected in the same foreach body. Eliminates the whack-a-mole pattern where fixing the first call unmasked the next on re-scan. Capped at 5 siblings per finding.
  • featureMarkdown action plan renders an 'Also in this loop' line per PERF-001 with siblings, so Claude (or a human) can plan all fixes in one pass.
  • featureNew --prune-baseline flag. Combined with --update-baseline, drops stale fingerprints whose rules no longer fire (e.g. when calibration releases retire false-positive patterns). Preserves reason/note annotations on the entries that do match.
  • improvementwriteBaseline stderr summary now reports 'N entries (M pruned, K preserved with reason/note)' or 'N entries (M stale retained — pass --prune-baseline to drop, K preserved with reason/note)'.
  • noteBackward-compatible: siblings is only emitted when non-empty, and existing JSON consumers (SARIF, diff.php, render.php) are unaffected. All v0.5.x baseline files remain valid.
v0.1.0Apr 20, 2026Initial release
  • feature13 rules across Security, Performance, AI-slop, and Deploy hygiene categories.
  • featureDeterministic, dependency-free PHP scanner. Runs on any Laravel 9/10/11/12 repo with PHP 8.1+.
  • featureScored HTML report with letter grade (A–F), severity-colored findings, and one-line fixes.
  • featureWhite-label support via config.json — add your brand name, URL, and call-to-action to every report.
  • featurePackaged as a Claude Code skill — drop into ~/.claude/skills/ and invoke conversationally.
  • noteDogfooded on 4 real Laravel codebases (2 private, 2 public) before launch to calibrate false-positive rates.