CVE-2026-45139
CI4MS Fileeditor allows deletion and rename of critical application files due to missing extension allowlist on destructive operations
CI4MS is a CodeIgniter 4-based content management system skeleton. Prior to version 0.31.9.0, the Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover. Version 0.31.9.0 patches the issue.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- High
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
6.5
CI4MS Fileeditor allows deletion and rename of critical application files due to missing extension allowlist on destructive operations
CI4MS is a CodeIgniter 4-based content management system skeleton. Prior to version 0.31.9.0, the Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover. Version 0.31.9.0 patches the issue.
- Record
CVE-2026-45139- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-73
- Updated
- 2026-11-02
EUVDEUVD-2026-45948
6.5
CI4MS is a CodeIgniter 4-based content management system skeleton. Prior to version 0.31.9.0, the Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover. Version 0.31.9.0 patches the issue.
- Record
EUVD-2026-45948- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-245j-xjvr-xvm5
6.5
CI4MS Fileeditor allows deletion and rename of critical application files due to missing extension allowlist on destructive operations
Summary
The Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover.
Details
Root cause: inconsistent application of the extension allowlist across Fileeditor operations in modules/Fileeditor/Controllers/Fileeditor.php.
The class declares an allowlist used by content-write operations:
// modules/Fileeditor/Controllers/Fileeditor.php:9
protected $allowedExtensions = ['css', 'js', 'html', 'txt', 'json', 'sql', 'md'];
// line 239
private function allowedFileTypes(string $file): bool
{
$extension = pathinfo($file, PATHINFO_EXTENSION);
if (!in_array(strtolower($extension), $this->allowedExtensions)) {
return false;
}
return true;
}
saveFile (line 110) and createFile (line 167) correctly call allowedFileTypes() against the target path before writing. The two destructive endpoints do not:
// deleteFileOrFolder — modules/Fileeditor/Controllers/Fileeditor.php:210-237
public function deleteFileOrFolder()
{
$valData = ([
'path' => ['label' => '', 'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-\.\/]+$/]'],
]);
if ($this->validate($valData) == false) return $this->fail($this->validator->getErrors());
$path = $this->request->getVar('path');
if ($this->isHiddenPath($path)) {
return $this->failForbidden();
}
$fullPath = realpath(ROOTPATH . $path);
if (!$fullPath || strpos($fullPath, realpath(ROOTPATH)) !== 0) {
return $this->response->setJSON(['error' => lang('Fileeditor.invalidFileOrFolder')])->setStatusCode(400);
}
if (is_dir($fullPath)) {
$result = rmdir($fullPath);
} else {
$result = unlink($fullPath); // executes on ANY extension
}
...
}
// renameFile — modules/Fileeditor/Controllers/Fileeditor.php:123-151
public function renameFile()
{
...
$path = $this->request->getVar('path');
if ($this->isHiddenPath($path)) {
return $this->failForbidden();
}
$newName = $this->request->getVar('newName');
$fullPath = realpath(ROOTPATH . $path);
$newPath = dirname($fullPath) . DIRECTORY_SEPARATOR . $newName;
if (!$this->allowedFileTypes($newName)) // <— only the destination is checked
return $this->failForbidden();
...
if (rename($fullPath, $newPath)) { ... } // source extension never validated
}
The validation gauntlet a path traverses before reaching unlink()/rename():
- Regex
/^[a-zA-Z0-9_ \-\.\/]+$/— admits any path made of alphanumerics, dots, dashes, underscores, slashes (matchesapp/Config/Routes.phptrivially). isHiddenPath()— only blocks paths whose individual segments equal an entry in$hiddenItems:
// modules/Fileeditor/Controllers/Fileeditor.php:10-26
protected $hiddenItems = [
'.git', '.github', '.idea', '.vscode', 'node_modules', 'vendor',
'writable', '.env', 'env', 'composer.json', 'composer.lock',
'tests', 'spark', 'phpunit.xml.dist', 'preload.php'
];
Critical CodeIgniter 4 framework files (app, Config, Routes.php, App.php, Database.php, Filters.php, public, index.php, .htaccess) are not members of this list, so they pass.
realpath+strposcontainment — confirms the…
- Record
GHSA-245j-xjvr-xvm5- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-73
- Updated
- 2026-11-02
NVD
6.5
CI4MS is a CodeIgniter 4-based content management system skeleton. Prior to version 0.31.9.0, the Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover. Version 0.31.9.0 patches the issue.
- Record
CVE-2026-45139- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-73
- Updated
- 2026-11-02
OSV
6.5
CI4MS Fileeditor allows deletion and rename of critical application files due to missing extension allowlist on destructive operations
CI4MS is a CodeIgniter 4-based content management system skeleton. Prior to version 0.31.9.0, the Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover. Version 0.31.9.0 patches the issue.
- Record
CVE-2026-45139- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-73
- Updated
- 2026-11-02
OSVGHSA-245j-xjvr-xvm5
6.5
CI4MS Fileeditor allows deletion and rename of critical application files due to missing extension allowlist on destructive operations
Summary
The Fileeditor module enforces an extension allowlist (['css','js','html','txt','json','sql','md']) on content-write operations (saveFile, createFile), but two destructive endpoints — deleteFileOrFolder and renameFile — never validate the extension of the source path. A backend user with file-editor permissions can therefore unlink or rename any file inside the project root that is not explicitly listed in the small $hiddenItems blocklist. Critical framework files such as app/Config/Routes.php, app/Config/App.php, app/Config/Database.php, app/Config/Filters.php, public/index.php, and public/.htaccess all live outside that blocklist and can be destroyed, producing a persistent denial of service that requires filesystem-level redeployment to recover.
Details
Root cause: inconsistent application of the extension allowlist across Fileeditor operations in modules/Fileeditor/Controllers/Fileeditor.php.
The class declares an allowlist used by content-write operations:
// modules/Fileeditor/Controllers/Fileeditor.php:9
protected $allowedExtensions = ['css', 'js', 'html', 'txt', 'json', 'sql', 'md'];
// line 239
private function allowedFileTypes(string $file): bool
{
$extension = pathinfo($file, PATHINFO_EXTENSION);
if (!in_array(strtolower($extension), $this->allowedExtensions)) {
return false;
}
return true;
}
saveFile (line 110) and createFile (line 167) correctly call allowedFileTypes() against the target path before writing. The two destructive endpoints do not:
// deleteFileOrFolder — modules/Fileeditor/Controllers/Fileeditor.php:210-237
public function deleteFileOrFolder()
{
$valData = ([
'path' => ['label' => '', 'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-\.\/]+$/]'],
]);
if ($this->validate($valData) == false) return $this->fail($this->validator->getErrors());
$path = $this->request->getVar('path');
if ($this->isHiddenPath($path)) {
return $this->failForbidden();
}
$fullPath = realpath(ROOTPATH . $path);
if (!$fullPath || strpos($fullPath, realpath(ROOTPATH)) !== 0) {
return $this->response->setJSON(['error' => lang('Fileeditor.invalidFileOrFolder')])->setStatusCode(400);
}
if (is_dir($fullPath)) {
$result = rmdir($fullPath);
} else {
$result = unlink($fullPath); // executes on ANY extension
}
...
}
// renameFile — modules/Fileeditor/Controllers/Fileeditor.php:123-151
public function renameFile()
{
...
$path = $this->request->getVar('path');
if ($this->isHiddenPath($path)) {
return $this->failForbidden();
}
$newName = $this->request->getVar('newName');
$fullPath = realpath(ROOTPATH . $path);
$newPath = dirname($fullPath) . DIRECTORY_SEPARATOR . $newName;
if (!$this->allowedFileTypes($newName)) // <— only the destination is checked
return $this->failForbidden();
...
if (rename($fullPath, $newPath)) { ... } // source extension never validated
}
The validation gauntlet a path traverses before reaching unlink()/rename():
- Regex
/^[a-zA-Z0-9_ \-\.\/]+$/— admits any path made of alphanumerics, dots, dashes, underscores, slashes (matchesapp/Config/Routes.phptrivially). isHiddenPath()— only blocks paths whose individual segments equal an entry in$hiddenItems:
// modules/Fileeditor/Controllers/Fileeditor.php:10-26
protected $hiddenItems = [
'.git', '.github', '.idea', '.vscode', 'node_modules', 'vendor',
'writable', '.env', 'env', 'composer.json', 'composer.lock',
'tests', 'spark', 'phpunit.xml.dist', 'preload.php'
];
Critical CodeIgniter 4 framework files (app, Config, Routes.php, App.php, Database.php, Filters.php, public, index.php, .htaccess) are not members of this list, so they pass.
realpath+strposcontainment — confirms the…
- Record
GHSA-245j-xjvr-xvm5- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-73
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
ci4-cms-erp/ci4msPackagist |
through 0.31.8.0 | 0.31.9.0 |
ci4-cms-erp/ci4msPackagist |
before 0.31.9.0, plus listed versions 0.21.00.21.10.21.20.21.30.21.3.1 Show 45 moreShow less0.21.3.20.21.3.30.21.3.40.21.3.50.21.3.60.21.3.70.23.0.00.23.0.10.23.0.20.23.1.00.24.0.00.24.0.160.24.0.180.24.0.190.24.0.200.24.0.270.24.0.420.24.0.450.24.0.600.25.0.00.25.0.10.25.0.20.25.0.300.25.0.390.25.0.430.25.1.00.25.2.00.25.3.00.26.0.00.26.1.00.26.2.00.26.3.00.26.3.10.26.3.20.26.3.30.26.3.40.27.0.00.28.0.00.28.3.00.28.4.00.28.5.00.28.6.00.31.0.00.31.1.00.31.2.0 |
0.31.9.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-45139 is a medium vulnerability (CVSS 6.5) in Ci4-cms-erp Ci4ms, published May 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.32%, the 25th percentile. Fixed in ci4-cms-erp/ci4ms 0.31.9.0.
References
github.com/ci4-cms-erp/ci4ms/security/advisories/GHSA-245j-xjvr-xvm5
github.com/ci4-cms-erp/ci4ms/releases/tag/0.31.9.0
github.com/advisories/GHSA-245j-xjvr-xvm5
github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45139.json
nvd.nist.gov/vuln/detail/CVE-2026-45139
github.com/ci4-cms-erp/ci4ms
Left out here: 7 versions and the rest of 2 source descriptions. The JSON record has them.