WireFileTools::render() normalizes the target filename to unix slashes, but compares it against the allowedPaths without normalizing those. On Windows a caller that builds an allowed path from __DIR__ gets backslashes, so the prefix comparison fails and a valid file is rejected with a WireFilesException.
The sibling method include() does not have this bug: it delegates the check to fileInPath(), which normalizes both sides.
Steps to reproduce
$dir = __DIR__; // C:\path\to (backslashes on Windows)
$wire->files->render(
$dir . '/target.php', // the standard PW idiom
[],
['allowedPaths' => [$dir . '/']] // same directory, so it must be allowed
);
Expected: the file renders.
Actual:
ProcessWire\WireFilesException: render: Filename C:/path/to/target.php is not in
an allowed path. Paths: C:\path\to/
Root cause
wire/core/WireFileTools/WireFileTools.php, render():
$options = array_merge($defaults, $options);
$filename = $this->unixFileName($filename); // forward slashes
...
} else if(strpos($filename, '/') !== false) {
// filename is absolute, make sure it's in a location we consider safe
$allowed = false;
foreach($options['allowedPaths'] as $path) {
if(strpos($filename, $path) === 0) {
$filename goes through unixFileName(); $path does not. On Windows __DIR__ yields \, so strpos($filename, $path) === 0 can never be true for a __DIR__-derived entry.
Suggested fix
Normalize allowedPaths in the same place the filename is normalized.
--- a/wire/core/WireFileTools/WireFileTools.php
+++ b/wire/core/WireFileTools/WireFileTools.php
@@ -1621,6 +1621,10 @@ class WireFileTools extends Wire {
$options = array_merge($defaults, $options);
$filename = $this->unixFileName($filename);
+ foreach($options['allowedPaths'] as $key => $allowedPath) {
+ $options['allowedPaths'][$key] = $this->unixDirName($allowedPath);
+ }
+
// add .php extension if filename doesn't already have an extension
WireFileTools::render()normalizes the target filename to unix slashes, but compares it against theallowedPathswithout normalizing those. On Windows a caller that builds an allowed path from__DIR__gets backslashes, so the prefix comparison fails and a valid file is rejected with aWireFilesException.The sibling method
include()does not have this bug: it delegates the check tofileInPath(), which normalizes both sides.Steps to reproduce
Expected: the file renders.
Actual:
Root cause
wire/core/WireFileTools/WireFileTools.php,render():$filenamegoes throughunixFileName();$pathdoes not. On Windows__DIR__yields\, sostrpos($filename, $path) === 0can never be true for a__DIR__-derived entry.Suggested fix
Normalize
allowedPathsin the same place the filename is normalized.