# Broken Access Control in AI OpenRouter Gateway: CVE Research

> A Filament gateway documents a config option to lock down its admin UI, but nothing in the code ever reads it. Any authenticated panel user reaches the full gateway: API tokens, system prompts, model settings and telemetry. Tracked as GHSA-57hh-5c98-83r8.

- **URL:** https://hackindex.io/research/broken-access-control-in-ai-openrouter-gateway
- **Authors:** [Joshua](https://hackindex.io/authors/joshua.md)
- **Published:** 2026-11-06
- **Updated:** 2026-11-07

**CWE-862 Missing Authorization**

AI OpenRouter Gateway, a Laravel and Filament package, ships a documented config option to restrict who may reach its admin UI. The option is read from the environment into config and never consumed anywhere, so the gate does nothing. Every authenticated Filament panel user, regardless of role, can reach the full gateway UI: mint API tokens, change system prompts, models and runtime settings, and read all invocation telemetry. Found during a HackIndex audit of open source Laravel packages.

## **Summary of the AI Gateway Authorization Issue**

The package exposes its admin UI through a Filament plugin: an integration registry, two settings pages, a token manager, an invocation log and an API docs page. Its config advertises a single switch, `filament.authorize`, described as the gate for the whole UI. An operator who reads the config and sets it, exactly as documented, gains no protection. Nothing in the package ever evaluates that value, and the sensitive pages carry no access check of their own. The intended restriction is silently absent.

## **How the AI Gateway Authorization Bug Works**

### **The config documents a gate for the whole UI**

[config/ai-gateway.php](https://github.com/andrecorugda/ai-openrouter-gateway/blob/150a7169c4/config/ai-gateway.php#L234-L240) defines the switch and documents exactly what it is for.

```php
'filament' => [
    'navigation_group' => 'AI & Automation',
    'navigation_sort' => 50,
    'cluster' => null,
    // Gate for the whole UI. A closure/invokable or a Gate ability name.
    // null = visible to anyone who can access the panel.
    'authorize' => env('AI_GATEWAY_FILAMENT_GATE', null),
],
```

An operator reads this, sets `AI_GATEWAY_FILAMENT_GATE=access-ai-gateway`, and defines that ability for admins only. The config now holds the ability name. The operator believes the UI is restricted to admins.

### **Nothing ever reads that value**

The plugin that registers the UI never consults `filament.authorize`. [src/Filament/AiGatewayPlugin.php](https://github.com/andrecorugda/ai-openrouter-gateway/blob/150a7169c4/src/Filament/AiGatewayPlugin.php#L39-L51) registers the resources and pages and stops there.

```php
public function register(Panel $panel): void
{
    $panel
        ->resources([
            AiIntegrationResource::class,
            AiInvocationResource::class,
        ])
        ->pages([
            GeneralSettings::class,
            ApiTokens::class,
            ApiDocs::class,
        ]);
}
```

A search across the package finds no read of `ai-gateway.filament.authorize` anywhere. The value travels from the environment into config and is never used to authorize anything.

### **The sensitive pages carry no access check**

Filament lets a resource or page gate itself with `canAccess()`. The gateway's sensitive surfaces do not. The token manager, the settings page and the integration registry define no `canAccess()`, so they inherit Filament's default: any user who can reach the panel can open them. The only page that defines `canAccess()` is the read-only API docs page, and it checks whether the HTTP API is enabled, not who the user is. The documented role gate is nowhere in that decision.

### **The result is unrestricted access**

Put together: the gate the config promises does not exist, and the pages have no fallback guard. Every authenticated panel user reaches every gateway page. On a site with more than one staff role, a low-privilege user who can sign into the panel for any unrelated reason gains full control of the AI gateway.

## **Who can do this**

Any user who can authenticate to the Filament panel, whatever their role. No admin rights, no special permission. The precondition is on the application, not the attacker: the site runs the gateway plugin and has more than one class of panel user, which is the exact scenario the `authorize` gate exists to handle. An operator who set the documented gate is still exposed, because the gate is never enforced.

## **Proof of Concept**

A panel with the plugin installed and the documented gate configured for admins only:

```php
// config: the gate is set exactly as documented
// AI_GATEWAY_FILAMENT_GATE=access-ai-gateway

// AppServiceProvider::boot(), the ability the operator defines
Gate::define('access-ai-gateway', fn (User $user) => $user->is_admin);
```

Signed in as a non-admin staff user, the gate correctly denies the ability, yet the page grants access:

```php
Gate::forUser($staff)->allows('access-ai-gateway')   // false
ApiTokens::canAccess()                               // true  (as $staff)
```

The non-admin user opens the AI Gateway navigation group and reaches every page: creates a Sanctum token carrying the gateway invoke ability, edits system prompts and model settings, and reads the full invocation log. The gate returns false and the UI opens anyway.

**Tested environment:**

- AI OpenRouter Gateway (`andrecorugda/ai-openrouter-gateway`), affected through the latest release v2.0.2
- Laravel Framework 12.71.0
- Filament 3.3.57
- PHP 8.5.12

## **Impact**

Broken access control over the entire AI gateway for any authenticated panel user. Such a user can:

- Mint Sanctum API tokens that carry the gateway invoke ability, then call the HTTP API as if authorized.
- Change system prompts, model selection and runtime settings, redirecting or poisoning every AI call the application makes.
- Read all invocation telemetry, including prompts and responses that may contain sensitive data.

The operator who wired the documented gate has no signal that it does nothing, so the exposure sits unnoticed.

## **Severity Assessment**

**CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N** (7.1, High)

| **Metric** | **Value** | **Reason** |
| --- | --- | --- |
| Attack Vector | Network | The panel is reached over HTTP. |
| Attack Complexity | Low | Open the page. No conditions to win. |
| Privileges Required | Low | Any authenticated panel user. |
| User Interaction | None | No second party needed. |
| Scope | Unchanged | The gateway UI and the panel share the application's own authorization context. |
| Confidentiality | Low | Configuration and invocation telemetry become readable. |
| Integrity | High | System prompts, models and settings can be rewritten, and API tokens minted. |
| Availability | None | No direct denial of the application itself. |

## **Fix for the AI Gateway Authorization Issue**

Enforce the gate the config already documents.

- In the plugin, read `config('ai-gateway.filament.authorize')` and apply it. Register the resources and pages behind that ability, or set each page's and resource's `canAccess()` to evaluate it.
- Where the value is a Gate ability name, call `Gate::allows()` with it. Where it is a closure or invokable, call it. Where it is null, keep the documented default of panel-wide visibility.
- Add a test that a user who fails the ability cannot reach the token, settings and integration pages, so the gate cannot silently regress.

## **References**

- Package on Packagist: [https://packagist.org/packages/andrecorugda/ai-openrouter-gateway](https://packagist.org/packages/andrecorugda/ai-openrouter-gateway)
- Source on GitHub: [https://github.com/andrecorugda/ai-openrouter-gateway](https://github.com/andrecorugda/ai-openrouter-gateway)
