CVE-2026-42845
Description
The form plugin for Grav adds the ability to create and use forms. Prior to 9.1.0 , there is an unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (md, yaml, yml, json, twig, ini) regardless of the configurable dangerous-extensions list. A permissive accept policy combined with the default destination: self@ could otherwise let an attacker overwrite the page's own .md and pivot to super-admin via a process: save action. This vulnerability is fixed in 9.1.0.
Affected products
1Patches
148bacc4187e1[security] block page-content overwrite via form upload (GHSA-w4rc-p66m-x6qq)
3 files changed · +27 −2
blueprints.yaml+1 −1 modified@@ -1,7 +1,7 @@ name: Form slug: form type: plugin -version: 9.0.3 +version: 9.1.0 description: Enables forms handling and processing icon: check-square author:
CHANGELOG.md+6 −0 modified@@ -1,3 +1,9 @@ +# v9.1.0 +## 04/29/2026 + +1. [](#bugfix) + * [security] Fixed unauthenticated page-content overwrite via file upload (GHSA-w4rc-p66m-x6qq). Public form uploads now strip path components from the POST-supplied filename and hard-block page-content extensions (`md`, `yaml`, `yml`, `json`, `twig`, `ini`) regardless of the configurable dangerous-extensions list. A permissive `accept` policy combined with the default `destination: self@` could otherwise let an attacker overwrite the page's own `.md` and pivot to super-admin via a `process: save` action. + # v9.0.3 ## 04/28/2026
classes/Form.php+20 −1 modified@@ -580,7 +580,11 @@ public function uploadFiles() $grav->fireEvent('onFormUploadSettings', new Event(['settings' => &$settings, 'post' => $post])); $upload = json_decode(json_encode($this->normalizeFiles($_FILES['data'], $settings->name)), true); - $filename = $post['filename'] ?? $upload['file']['name']; + // Strip any path component from the POST-supplied filename. The admin + // controllers already do this; the public form path historically did + // not, which let an attacker collide the upload with files outside + // the intended destination. + $filename = Utils::basename((string) ($post['filename'] ?? $upload['file']['name'])); $field = $upload['field']; // Handle errors and breaks without proceeding further @@ -605,6 +609,21 @@ public function uploadFiles() ]; } + // Hard-block page-content extensions regardless of the configurable + // dangerous-extensions list. With destination: self@ (the default), + // an upload lands in the page directory, and a permissive accept + // policy would otherwise let an unauthenticated user overwrite the + // page's own .md/.yaml — turning a file upload into arbitrary + // page-content takeover (GHSA-w4rc-p66m-x6qq). + $extension = strtolower((string) pathinfo($filename, PATHINFO_EXTENSION)); + if (in_array($extension, ['md', 'yaml', 'yml', 'json', 'twig', 'ini'], true)) { + return [ + 'status' => 'error', + 'message' => sprintf($language->translate('PLUGIN_FORM.FILEUPLOAD_UNABLE_TO_UPLOAD', null), + $filename, 'File type not allowed') + ]; + } + if (!isset($settings->destination)) { return [ 'status' => 'error',
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
4News mentions
0No linked articles in our index yet.