Every CVE Search/ Get API key

CVE-2026-74907

Grav before 2.0.15 Path Traversal via plugin-asset-map.php

8.2 HighFix available

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.43%Up 0.1 points since Aug 19, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.2

Grav before 2.0.15 Path Traversal via plugin-asset-map.php

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
CVE-2026-74907
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-60588 8.2

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
EUVD-2026-60588
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-2rhw-8953-48q3 8.2

Duplicate Advisory: Grav: Unauthenticated Path Traversal via Missing Directory-Boundary Check in `plugin-asset-map.php` Static Asset Server (`index.php`)

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-4v9q-p283-qc2m. This link is maintained to preserve external references.

Original Description

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
GHSA-2rhw-8953-48q3
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
GitHub AdvisoriesGHSA-4v9q-p283-qc2m 8.2

Grav: Unauthenticated Path Traversal via Missing Directory-Boundary Check in `plugin-asset-map.php` Static Asset Server (`index.php`)

Verified against: getgrav/grav devel branch, GRAV_VERSION = "2.0.15", file `index.php

Title

Unauthenticated Path Traversal via Missing Directory-Boundary Check in plugin-asset-map.php Static Asset Server (index.php)

Product / Affected Versions

  • Product: getgrav/grav
  • File: index.php (top-level front controller, runs before Grav itself boots)
  • Confirmed present in: devel branch, 2.0.15
  • Precondition: requires user/config/plugin-asset-map.php to exist and contain at least one route-prefix mapping ,this is an opt-in mechanism (per the code comment: "Fast static asset serving for plugins that bundle SPA apps"). No core mechanism generates this file automatically; it's created by a plugin that opts into this fast-path. Not reachable on a stock Grav install with no such plugin. Where reachable, it requires zero authentication.

CWE

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') , specific mechanism: a path-prefix containment check performed with plain string comparison (str_starts_with) instead of a directory-boundary-aware comparison, allowing escape into any sibling path whose name happens to extend the base directory's name as a string.

Description

index.php implements a fast-path static file server that runs before Grav's own routing/security stack, gated on the presence of an asset-map file:

$assetMapFile = __DIR__ . '/user/config/plugin-asset-map.php';
if (is_file($assetMapFile)) {
    $assetMap = require $assetMapFile;
    foreach ($assetMap as $routePrefix => $diskPath) {
        if (str_starts_with($path, $routePrefix)) {
            $relPath = substr($path, strlen($routePrefix));
            $filePath = __DIR__ . '/' . ltrim($diskPath, '/') . $relPath;
            $realFile = realpath($filePath);
            $realBase = realpath(__DIR__ . '/' . ltrim($diskPath, '/'));
            if ($realFile && $realBase && str_starts_with($realFile, $realBase) && is_file($realFile)) {
                // ... serves $realFile directly, with Content-Type inferred from extension
                readfile($realFile);
                exit;
            }
        }
    }
}

realpath() correctly resolves .. sequences, so a naive ../../etc/passwd-style traversal that leaves the filesystem entirely is blocked (it wouldn't share the $realBase string prefix). But the containment check itself, str_starts_with($realFile, $realBase), has no directory-boundary awareness it's a plain string-prefix test, not "is $realFile inside the $realBase directory." Any resolved path whose string representation merely begins with the same characters as $realBase passes, including sibling directories that extend the base directory's name (assets → assets-secret, assets.bak, assets_old, assets2, etc.) a very common real-world directory-naming pattern (backup dirs, versioned dirs, disabled/legacy dirs sitting alongside the active one).

Live Proof of Concept

Setup: the exact code block above, extracted verbatim from index.php, executed with PHP 8.3.6 against a realistic directory layout (a plugin's active assets/ dir sitting next to an unrelated assets-secret/ dir containing a fake secret):

user/plugins/myplugin/assets/app.js            <- intended, public
user/plugins/myplugin/assets-secret/config.php <- NOT intended to be served
user/config/plugin-asset-map.php:
    return ['/myplugin-assets' => 'user/plugins/myplugin/assets'];

Legitimate request (/myplugin-assets/app.js):

realFile: '/home/claude/grav-poc/user/plugins/myplugin/assets/app.js'
realBase: '/home/claude/grav-poc/user/plugins/myplugin/assets'
>>> WOULD SERVE FILE <<<
>>> Content: public asset content

Traversal request (/myplugin-assets/../assets-secret/config.php):

realFile: '/home/claude/grav-poc/user/plugins/myplugin/assets-secret/config.php'
realBase: '/home/claude/grav-poc/user/plugins/myplugin/assets'
>>> WOULD SERVE…
Record
GHSA-4v9q-p283-qc2m
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
NVD 8.2

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
CVE-2026-74907
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 8.2

Grav before 2.0.15 Path Traversal via plugin-asset-map.php

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
CVE-2026-74907
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-2rhw-8953-48q3 8.2

Duplicate Advisory: Grav: Unauthenticated Path Traversal via Missing Directory-Boundary Check in `plugin-asset-map.php` Static Asset Server (`index.php`)

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-4v9q-p283-qc2m. This link is maintained to preserve external references.

Original Description

Grav before 2.0.15 contains a path traversal vulnerability in the static asset server within index.php that uses string prefix matching instead of directory-boundary validation. Unauthenticated attackers can access files in sibling directories by exploiting directory names that extend the base path string, such as requesting assets-secret when assets is the configured base.

Record
GHSA-2rhw-8953-48q3
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-4v9q-p283-qc2m 8.2

Grav: Unauthenticated Path Traversal via Missing Directory-Boundary Check in `plugin-asset-map.php` Static Asset Server (`index.php`)

Verified against: getgrav/grav devel branch, GRAV_VERSION = "2.0.15", file `index.php

Title

Unauthenticated Path Traversal via Missing Directory-Boundary Check in plugin-asset-map.php Static Asset Server (index.php)

Product / Affected Versions

  • Product: getgrav/grav
  • File: index.php (top-level front controller, runs before Grav itself boots)
  • Confirmed present in: devel branch, 2.0.15
  • Precondition: requires user/config/plugin-asset-map.php to exist and contain at least one route-prefix mapping ,this is an opt-in mechanism (per the code comment: "Fast static asset serving for plugins that bundle SPA apps"). No core mechanism generates this file automatically; it's created by a plugin that opts into this fast-path. Not reachable on a stock Grav install with no such plugin. Where reachable, it requires zero authentication.

CWE

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') , specific mechanism: a path-prefix containment check performed with plain string comparison (str_starts_with) instead of a directory-boundary-aware comparison, allowing escape into any sibling path whose name happens to extend the base directory's name as a string.

Description

index.php implements a fast-path static file server that runs before Grav's own routing/security stack, gated on the presence of an asset-map file:

$assetMapFile = __DIR__ . '/user/config/plugin-asset-map.php';
if (is_file($assetMapFile)) {
    $assetMap = require $assetMapFile;
    foreach ($assetMap as $routePrefix => $diskPath) {
        if (str_starts_with($path, $routePrefix)) {
            $relPath = substr($path, strlen($routePrefix));
            $filePath = __DIR__ . '/' . ltrim($diskPath, '/') . $relPath;
            $realFile = realpath($filePath);
            $realBase = realpath(__DIR__ . '/' . ltrim($diskPath, '/'));
            if ($realFile && $realBase && str_starts_with($realFile, $realBase) && is_file($realFile)) {
                // ... serves $realFile directly, with Content-Type inferred from extension
                readfile($realFile);
                exit;
            }
        }
    }
}

realpath() correctly resolves .. sequences, so a naive ../../etc/passwd-style traversal that leaves the filesystem entirely is blocked (it wouldn't share the $realBase string prefix). But the containment check itself, str_starts_with($realFile, $realBase), has no directory-boundary awareness it's a plain string-prefix test, not "is $realFile inside the $realBase directory." Any resolved path whose string representation merely begins with the same characters as $realBase passes, including sibling directories that extend the base directory's name (assets → assets-secret, assets.bak, assets_old, assets2, etc.) a very common real-world directory-naming pattern (backup dirs, versioned dirs, disabled/legacy dirs sitting alongside the active one).

Live Proof of Concept

Setup: the exact code block above, extracted verbatim from index.php, executed with PHP 8.3.6 against a realistic directory layout (a plugin's active assets/ dir sitting next to an unrelated assets-secret/ dir containing a fake secret):

user/plugins/myplugin/assets/app.js            <- intended, public
user/plugins/myplugin/assets-secret/config.php <- NOT intended to be served
user/config/plugin-asset-map.php:
    return ['/myplugin-assets' => 'user/plugins/myplugin/assets'];

Legitimate request (/myplugin-assets/app.js):

realFile: '/home/claude/grav-poc/user/plugins/myplugin/assets/app.js'
realBase: '/home/claude/grav-poc/user/plugins/myplugin/assets'
>>> WOULD SERVE FILE <<<
>>> Content: public asset content

Traversal request (/myplugin-assets/../assets-secret/config.php):

realFile: '/home/claude/grav-poc/user/plugins/myplugin/assets-secret/config.php'
realBase: '/home/claude/grav-poc/user/plugins/myplugin/assets'
>>> WOULD SERVE…
Record
GHSA-4v9q-p283-qc2m
CVSS 4.0
8.2 HighCVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
getgrav/grav
Packagist
through 2.0.14
2.0.15
getgrav/grav
Packagist
before 2.0.15, plus listed versions
0.8.00.9.00.9.10.9.100.9.11
Show 45 moreShow less
0.9.120.9.130.9.140.9.150.9.160.9.170.9.180.9.190.9.20.9.200.9.210.9.220.9.230.9.240.9.250.9.260.9.270.9.280.9.290.9.30.9.300.9.310.9.320.9.330.9.340.9.350.9.360.9.370.9.380.9.390.9.40.9.400.9.410.9.420.9.430.9.440.9.450.9.50.9.60.9.70.9.80.9.91.0.01.0.0-rc.11.0.0-rc.2
2.0.15

History

History
RecordedChangedSources
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-74907 is a high vulnerability (CVSS 8.2) in Getgrav Grav, published August 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in getgrav/grav 2.0.15.

References

This page doesn't list everything

Left out here: 299 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open