Skip to content

Roles mode returns 500 for any request that arrives without the roles header #1604

Description

@azuretek

Summary

With AUTH_ROLES_ENABLE=true, any request that reaches Heimdall without the roles header returns HTTP 500. The header is read out of $_SERVER with no guard, inside a view()->composer('*') callback, so it takes down every page rather than the one route that needs the roles.

Root cause

app/Providers/AppServiceProvider.php, in the composer registered for all views:

if (config('app.auth_roles_enable')) {
    $view->with('enable_auth_admin_controls', in_array(config('app.auth_roles_admin'), explode(config('app.auth_roles_delimiter'), $_SERVER[config('app.auth_roles_http_header')])));
}

$_SERVER[config('app.auth_roles_http_header')] is unconditional. On PHP 8 a missing key raises ErrorException, and because the composer is registered against * there is no view that can render without it.

ItemController::dash() reads the same header the other way, through the request object, which returns null rather than throwing:

$roles = explode(config('app.auth_roles_delimiter'), $request->header(config('app.auth_roles_header')));

That one does not fatal, but it passes null to explode(), which has been deprecated as a string argument since PHP 8.1.

Observed

Heimdall 2.8.2 (linuxserver/heimdall:2.8.2-ls362):

$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
500

storage/logs/laravel-*.log:

[2026-09-08 23:05:32] local.ERROR: Undefined array key "HTTP_X_TOKEN_USER_ROLES" {"exception":"[object] (ErrorException(code: 0): Undefined array key "HTTP_X_TOKEN_USER_ROLES" at /app/www/app/Providers/AppServiceProvider.php:95)

Reproduce

  1. Set AUTH_ROLES_ENABLE=true and the other AUTH_ROLES_* variables.
  2. Request any view route without the roles header, for example on the container's published port rather than through the proxy.
  3. HTTP 500, with the log line above.

Why it survived

In a working deployment the proxy sets the header on every request that reaches the app, so the key is never missing in the path anyone exercises. It goes missing only where something bypasses the proxy: a container healthcheck, a monitoring probe on the published port, a request during the window before the proxy is configured, or a direct hit on the LAN address. Each of those returns 500 rather than a page, and in the last case that is the whole first impression of a new roles-mode setup.

Proposed fix

Coalesce the missing header to an empty string, which fails closed on the admin check immediately below it: no header means no roles, never the admin role. Do the same for $request->header(...) in dash() so the deprecated explode(string, null) goes away with it.

PR to follow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions