CVE-2026-62667 Overview
CVE-2026-62667 is a broken access control vulnerability [CWE-862] in the Grav API Plugin, a RESTful API for Grav CMS that provides headless access to site content. The flaw exists in versions prior to 1.0.6. The ApiKeyManager::generateKey() method stores a declared scopes array, but ApiKeyAuthenticator::authenticate() never reads keyData[scopes] when validating a key. As a result, AbstractApiController::requirePermission() evaluates the owning user's full Access Control List (ACL). An API key issued for a read-only scope can perform every write, delete, and administrative operation available to the key owner.
Critical Impact
A low-privileged API key with read-only scope can escalate to the full permissions of its owning account, including super-admin actions when the key was minted on an administrative user.
Affected Products
- Grav CMS API Plugin versions prior to 1.0.6
- Grav CMS deployments issuing scoped API keys
- Headless Grav installations relying on API key scope enforcement
Discovery Timeline
- 2026-08-19 - CVE-2026-62667 published to the National Vulnerability Database (NVD)
- 2026-08-19 - Last updated in NVD database
Technical Details for CVE-2026-62667
Vulnerability Analysis
The Grav API plugin implements scoped API keys, allowing administrators to mint keys restricted to a subset of the owner's permissions. The authentication layer supports issuing keys with declared scopes such as read or specific resource operations. However, the authenticator discards these scopes at validation time.
When a request arrives with an API key, ApiKeyAuthenticator::authenticate() returns the owning user object without attaching the key's scopes metadata to the request context. Downstream, AbstractApiController::requirePermission() calls the standard ACL check against the returned user identity. Because no scope filter is applied, the request executes with the full ACL of the owning account. A key marked read-only therefore gains write, delete, and administrative capabilities in practice.
The upstream fix in commit dfcc947 introduces a request-local authenticatedScopes field on the authenticator and stamps requests with an api_key_scopes attribute for downstream enforcement.
Root Cause
The root cause is missing authorization enforcement [CWE-862]. Key metadata containing declared scopes is persisted but not consulted during authentication or permission checks. The vulnerability represents a classic authorization gap between policy declaration and policy enforcement layers.
Attack Vector
An attacker with any valid scoped API key can issue arbitrary API requests over the network. No user interaction is required. The attack works whenever the API key was issued on an account with elevated privileges, including super-admin accounts. Exploitation requires only network access to the Grav API endpoint and possession of a scoped key.
// Security patch in classes/Api/Auth/ApiKeyAuthenticator.php
// Adds request-local scope tracking so downstream controllers can enforce
// the key's declared scopes rather than the owner's full ACL.
class ApiKeyAuthenticator implements AuthenticatorInterface
{
/**
* Scopes of the key that last authenticated successfully, or null if none
* has. The AuthMiddleware reads this immediately after authenticate() to
* stamp the request with `api_key_scopes` so requirePermission() can cap a
* scoped key to exactly its declared permissions (GHSA-x7hm). A fresh
* authenticator instance is built per request, so this is request-local.
*
* @var array<int, mixed>|null
*/
private ?array $authenticatedScopes = null;
public function __construct(
protected readonly Grav $grav,
) {}
/**
* Scopes recorded for the most recent successful authenticate() call.
* An empty array means an unscoped key (full account access).
*
* @return array<int, mixed>
*/
public function getAuthenticatedScopes(): array
{
return $this->authenticatedScopes ?? [];
}
public function authenticate(ServerRequestInterface $request): ?UserInterface
Source: GitHub Commit dfcc947
The companion patch in AbstractApiController.php enforces the scope cap before the super-admin short-circuit, ensuring scoped keys minted on administrative accounts remain restricted:
// Security patch in classes/Api/Controllers/AbstractApiController.php
{
$user = $this->getUser($request);
// API-key scope cap (GHSA-x7hm). A key created with a NON-EMPTY `scopes`
// list is restricted to exactly those permissions, regardless of the
// owning account's ACL — so a scoped key minted on a super-admin account
// is still capped. This is enforced BEFORE the super-admin short-circuit
// below so super keys can't bypass it. An empty/absent scope set (the
// default, and all JWT/session credentials) means full access.
$scopes = $request->getAttribute('api_key_scopes');
if (is_array($scopes) && $scopes !== [] && !$this->scopesPermit($scopes, $permission)) {
throw new ForbiddenException("API key is not authorized for: {$permission}");
}
// Super admin can do anything
if ($this->isSuperAdmin($user)) {
return;
Source: GitHub Commit dfcc947
Detection Methods for CVE-2026-62667
Indicators of Compromise
- API requests using a scoped API key that perform write, delete, or administrative operations inconsistent with the key's declared scope
- Sudden surges of POST, PUT, PATCH, or DELETE requests originating from keys previously observed only issuing GET requests
- Unexpected content modifications, user creations, or plugin configuration changes correlated with API key authentication events
Detection Strategies
- Audit Grav access logs for API key IDs and correlate the observed HTTP methods against the scopes recorded at key creation time in user/data/api-keys or equivalent storage
- Identify authenticated requests to administrative endpoints such as /api/users, /api/config, or /api/plugins that trace back to keys not intended for administrative use
- Compare pre-1.0.6 baseline API traffic to post-upgrade traffic to identify keys that were relying on the broken enforcement
Monitoring Recommendations
- Enable verbose logging on the Grav API plugin and forward logs to a centralized SIEM for retention and analysis
- Alert on any API key performing its first write, delete, or administrative operation
- Track the ratio of read to write operations per API key and flag statistical outliers
How to Mitigate CVE-2026-62667
Immediate Actions Required
- Upgrade the Grav API Plugin to version 1.0.6 or later without delay
- Rotate all existing API keys after upgrading, treating any scoped key issued on a privileged account as potentially abused
- Review Grav audit logs for administrative actions performed via API keys since the plugin was first deployed
Patch Information
The issue is fixed in Grav API Plugin version 1.0.6. The fix enforces API key scopes at authentication and stamps the request with an api_key_scopes attribute that requirePermission() evaluates before the super-admin short-circuit. See the GitHub Release 1.0.6 and the GitHub Security Advisory GHSA-x7hm-jc32-v39j for details.
Workarounds
- If immediate upgrade is not possible, revoke all scoped API keys and issue only unscoped keys on accounts that already hold the full set of intended permissions
- Restrict network access to the Grav API endpoints using a reverse proxy or Web Application Firewall (WAF) rule that limits allowed HTTP methods per API key or client IP
- Temporarily disable the Grav API Plugin if scoped keys cannot be replaced or gated at the network layer
# Upgrade the Grav API Plugin to the patched release
bin/gpm update api
# Verify the installed version is 1.0.6 or later
bin/gpm info api | grep -i version
# Rotate existing API keys after the upgrade completes
bin/plugin api keys:list
bin/plugin api keys:revoke <key-id>
bin/plugin api keys:create --scopes=read
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

