Skip to content

False positive: laravel/horizon classified as a development package #26

Description

@jszobody

Description

DebugModeAuditService classifies laravel/horizon as a "development package" and flags it in production. It also flags any registered route whose URI starts with horizon/ as an "exposed testing route" without checking whether the route is protected by middleware.

Both findings are false positives for any project using Horizon as intended. Horizon is a production tool — it's the dashboard for monitoring and managing Laravel queue workers in production environments — and it is meant to be installed alongside the workers it observes. The Horizon docs cover production deployment specifically (supervisor configuration, dashboard authorization, snapshot scheduling). The dashboard route is gated via Gate::define('viewHorizon', ...) in the app's HorizonServiceProvider, which is the standard pattern.

The current behavior produces two findings per audit on every healthy Laravel application that uses Horizon.

Steps to Reproduce

  1. Install laravel/horizon in a Laravel app (production-style: require, not require-dev).
  2. Set APP_ENV=production, ensure Horizon's routes are registered, and gate the dashboard via the standard viewHorizon Gate.
  3. Run php artisan warden:audit.
  4. Two findings appear:
    • debug-mode │ laravel/horizon │ Development package detected in production (severity: high)
    • debug-mode │ routes │ Testing routes are exposed (severity: high) — triggered by horizon/* URIs

Expected Behavior

  • Horizon is not flagged as a development package, because it isn't one.
  • horizon/* routes are not flagged as exposed when they are protected by auth/gate middleware (or, at minimum, Horizon's routes specifically are not flagged at all, since the package is intended for production use).

Current Behavior

In src/Services/Audits/DebugModeAuditService.php, Horizon is hardcoded into the dev-packages list:

private array $devPackages = [
    'barryvdh/laravel-debugbar',
    'laravel/telescope',
    'laravel/horizon',          // ← misclassified
    'beyondcode/laravel-dump-server',
    'laravel/dusk',
];

And hasExposedTestingRoutes() flags any horizon/* route purely by URI prefix:

$testingRoutes = [
    'telescope',
    'horizon',
    '_dusk',
];

foreach ($testingRoutes as $testingRoute) {
    if (str_starts_with($uri, $testingRoute)) {
        return true;
    }
}

No inspection of the route's middleware stack, so a properly auth-gated Horizon dashboard is still flagged.

Suggested Fix

I'd propose two complementary changes:

1. Remove Horizon from both lists

Horizon is a legitimate production dependency. The simplest fix is dropping it from $devPackages and from the $testingRoutes URI list in hasExposedTestingRoutes(). Telescope and Dusk are reasonable to keep flagged by default; Horizon is not.

2. Add a config-driven suppression mechanism for findings

Beyond Horizon specifically, there's no general way for users to acknowledge a finding they've reviewed and accepted (e.g., a team that runs Telescope in production intentionally, behind a gate, for observability). A suppression config would solve this cleanly and avoid future "is X really a dev package?" debates.

Proposed config shape in config/warden.php:

'ignore_findings' => [
    // Match by source + package
    ['source' => 'debug-mode', 'package' => 'laravel/horizon'],

    // Or by source + title pattern
    ['source' => 'debug-mode', 'title' => 'Testing routes are exposed'],
],

AbstractAuditService::addFinding() (or a filter in AuditExecutor) would skip findings that match any entry. Matching could be exact or simple wildcard. This gives users a documented, reviewable way to silence accepted false positives without forking.

Happy to put up a PR for either or both if there's interest — let me know which direction you'd prefer.

Environment

  • Warden: v1.5.1
  • Laravel: 13.x
  • PHP: 8.5

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions