Where
-Infinity
0
Severity
9.3
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Grav API plugin (getgrav/grav-plugin-api) before 1.0.14 contains a missing authorization vulnerability in userPassesAuthorize() (AbstractApiController.php). The function fails to consult the calling request's API key scopes, relying instead on the account's raw super-admin flag and ACL grants. As a result, an authenticated attacker holding a scoped API key minted on a privileged account can bypass their declared scope restrictions to access authorize-gated UI metadata and item definitions (sidebar/menubar/widget items and users-list columns/row-actions/filter-tabs) that their key scope should deny, resulting in information disclosure.

First published (updated )
Severity
9.3
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

The getgrav/grav-plugin-api plugin before 1.0.18 does not enforce API-key scope in the requireNotSuperTarget() function in UsersController.php across seven sensitive user-management endpoints. The check uses isSuperAdmin() on the acting account rather than verifying whether the specific API key carries super authority (via isSuperWithinScope()). As a result, an API key scoped below full super authority but belonging to a super-admin account can act against other super-admin accounts—disabling their 2FA, deleting their avatar, minting new API keys under their identity, or deleting their existing API keys.

First published (updated )
Severity
8.7
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

The getgrav/grav-plugin-api plugin before 1.0.13 fails to validate that the scopes of a newly created API key are a subset of the caller's scopes in createApiKey. The self-target path of requireApiKeyPermission() requires only the baseline api.access scope, and the new key's scopes are read directly from the request body with no subset check. An attacker holding a minimal-scope API key on a super account can submit an empty scopes array to mint an unscoped, full-access super key, bypassing scope restrictions (and enabling further chains such as configuration write to RCE).

First published (updated )
Severity
8.7
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

The Grav API plugin (getgrav/grav-plugin-api, bundled with Grav's admin-next/API stack) before 1.0.14 fails to enforce the authorize requirement in MenubarController::executeAction(). While the GET /menubar/items listing endpoint correctly filters menubar items via userPassesAuthorize(), the POST /api/v1/menubar/actions/{plugin}/{action} endpoint only checks the baseline api.access permission and never evaluates the authorize field a plugin registered for that action. Any authenticated caller with api.access can therefore invoke a privileged menubar action directly, bypassing the intended authorization. No plugin bundled with core Grav currently registers a privileged authorize handler, so on a stock install the impact is latent; the flaw affects any first- or third-party plugin relying on the documented authorize semantics.

First published (updated )
Severity
8.6
AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Grav Plugin API (getgrav/grav-plugin-api) before 1.0.13 fails to enforce API-key scope caps in InvitationsController. The strip-super and accept-groups decisions are gated on a bare isSuperAdmin() check rather than a scope-aware permission check, so a least-privilege API key (scoped to api.users.write) minted on a super account can create an invitation record containing super-admin access flags. When the invitation is accepted, those flags are written verbatim to the new account, resulting in privilege escalation to a fully controlled super account.

First published (updated )
Severity
8.6
AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N

The Grav API plugin (getgrav/grav-plugin-api, bundled with Grav 2.0's admin-next/API stack) before version 1.0.14 contains an open redirect weakness in SsoController::sanitizeReturnTo(). The function rejects a literal '//' prefix but does not account for browsers normalizing backslashes to slashes in special (http/https) schemes, so a returnTo value such as '/\evil.com' passes the guard and is later resolved by the browser as the protocol-relative URL '//evil.com'. Following a legitimate OAuth login flow, an attacker-supplied returnTo parameter could redirect an authenticated victim to an attacker-controlled site for post-login phishing. Full browser-side exploitability depends on the admin-next SPA's client-side oauth-callback handler and was not independently verified by the reporter.

First published (updated )
Severity
7.2
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L

The getgrav/grav-plugin-api plugin before 1.0.13 contains an API-key scope cap bypass in the POST /reports/twig-content/allowlist endpoint (ReportsController). The endpoint enforces requirePermission('api.config.write') followed by a bare isSuperAdmin() check instead of requireSuper(). Because isSuperAdmin() reads access.api.super directly and never consults apikeyscopes, a least-privilege API key scoped to api.config.write minted on a super account passes the gate, allowing an attacker to append attacker-chosen tokens to the security.twigsandbox allowlist (persisted to user/config/security.yaml). Widening the allowlist turns any subsequent Twig-in-content render into an SSTI/RCE sink.

First published (updated )
Severity
7.1
Path Traversal
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

grav-plugin-api (getgrav/grav-plugin-api) versions >= 1.0.0-beta.10 and <= 1.0.14 contain a path traversal vulnerability in the PagesController::batchCopy() method. An incomplete fix for GHSA-qjq4-jp55-4mx2 left the user-controlled 'suffix' parameter (via POST /api/v1/pages/batch) unvalidated. An authenticated user with the api.pages.write permission (editor-level, not super-admin) can supply path traversal sequences (e.g. /../../../) in the suffix parameter to escape the intended user/pages/ directory and write attacker-controlled page content and page media to arbitrary filesystem locations writable by the web server process. The vulnerability is fixed in 1.0.15.

First published (updated )
Severity
6.9
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

The Grav API plugin (getgrav/grav-plugin-api) before 2.0.4 contains an improper session invalidation vulnerability where JWT access tokens are issued without a jti (JWT ID) claim and therefore cannot be revoked server-side. Unlike refresh tokens, access tokens remain valid for their full lifetime (default 1 hour) regardless of logout, password change, new token issuance, or account disablement. An attacker who has stolen an access token retains full API access until the token naturally expires.

First published (updated )
Severity
5.3
Malicious File Upload
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

The Grav API plugin (getgrav/grav-plugin-api) before 1.0.3 contains a file upload extension bypass in the API media controller. HandlesMediaUploads::validateFileExtension() inspects only the final file extension via pathinfo($filename, PATHINFOEXTENSION), so a user with api.media.write permission can upload a file with a double extension such as shell.php.jpg to bypass the dangerous extensions blocklist. The web server may then execute the file as PHP, resulting in remote code execution.

First published (updated )
Severity
5.3
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L

The Grav API plugin (getgrav/grav-plugin-api) before 1.0.13 contains an API-key scope cap bypass in DemoController. Its private requireSuper() method checks isSuperAdmin() and returns early before invoking requirePermission(), so the apikeyscopes cap (enforced only in requirePermission()) is skipped. As a result, any scoped API key minted on a super account can bypass its scope restrictions when calling the baseline() and reset() operations (e.g. POST /api/v1/demo/reset), allowing it to capture the demo baseline or force a demo reset. Impact is bounded to demo-engine control and is conditional on demo mode being configured with writable resources.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203