mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-11 22:38:54 +00:00
Set the directory permission Flysystem actually reads
FILES_WEB_SERVER_READABLE asks for 0755 on the directories a download has to be traversed through, and asks for it from a key that is never consulted. FilesystemManager::createLocalDriver passes `directory_visibility ?? visibility ?? private` to PortableVisibilityConverter::fromArray() as the default visibility for directories. This disk sets `visibility` to public two lines above, and no `directory_visibility`, so directories are public and the converter reads `dir.public`. The configuration names only `dir.private`. The mode is 0755 regardless, because 0755 is Flysystem's default for a public directory -- the right answer from the wrong place. Adding `directory_visibility` to this disk, or a change to that default, is all it would take for the flag to stop doing what it says. Measured on main, with the flag on: dir.private 0755 → 0750 directory stays 0755 (nothing reads it) dir.public 0755 → 0750 directory becomes 0750 (this is the key) Both are named now, so the intent survives either way round. FilePermissionsTest could not have caught this, because it was not testing this configuration. filesDiskWith() restated the shipped branch inline, verbatim down to the 0755, so it went on passing against its own copy however the real one changed. It now requires config/filesystems.php and replaces only the root, which is what makes the mutation above visible to it. Two more things in the same helper, both about the suite rather than the subject: the scratch root is per worker now (Tests\TestCase does the same for upload parts, and eight workers sharing one directory means one worker's afterEach deletes another's tree mid-test), and it is cleared before each test as well as after, so a killed run does not poison the next one.
This commit is contained in:
+16
-1
@@ -62,12 +62,27 @@ return [
|
||||
// umask 0077 still produces 0700 and still cannot be
|
||||
// traversed; INSTALL.md covers fixing that, because it cannot
|
||||
// be fixed from this file.
|
||||
//
|
||||
// Both directory keys, because which one Flysystem reads is
|
||||
// decided elsewhere: FilesystemManager passes
|
||||
// `directory_visibility ?? visibility ?? private` as the
|
||||
// default visibility for directories, so with `visibility`
|
||||
// public just below, it reads `dir.public` and never looks at
|
||||
// `dir.private`. Naming only the private one asked for 0755
|
||||
// from a key nobody consults, and got 0755 anyway because that
|
||||
// is Flysystem's default for a public directory — the right
|
||||
// answer from the wrong place, which is the kind that stops
|
||||
// being right quietly. Adding `directory_visibility` here, or
|
||||
// a change to that default, would have been enough.
|
||||
// Spread rather than two ternaries so that leaving the flag
|
||||
// off is not merely equivalent to the old configuration but
|
||||
// literally it — no install that does not need this sees its
|
||||
// file modes change.
|
||||
...(env('FILES_WEB_SERVER_READABLE', false)
|
||||
? ['visibility' => 'public', 'permissions' => ['dir' => ['private' => 0755]]]
|
||||
? [
|
||||
'visibility' => 'public',
|
||||
'permissions' => ['dir' => ['public' => 0755, 'private' => 0755]],
|
||||
]
|
||||
: []),
|
||||
],
|
||||
|
||||
|
||||
@@ -15,26 +15,46 @@ declare(strict_types=1);
|
||||
// ceiling there rather than a guarantee. That asymmetry is invisible from the
|
||||
// configuration and is exactly what someone would "simplify" away.
|
||||
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\ParallelTesting;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Points the `files` disk at a scratch root, configured the way
|
||||
* This worker's scratch root.
|
||||
*
|
||||
* Per worker, like the upload parts directory in Tests\TestCase: eight of
|
||||
* them run this file at once, and the afterEach below deletes the tree it
|
||||
* is given. Shared, one worker's cleanup lands in the middle of another
|
||||
* worker's test, and the failure is a mode read from a directory that was
|
||||
* removed underneath it.
|
||||
*/
|
||||
function filesPermissionRoot(): string
|
||||
{
|
||||
return storage_path('app/files-permission-test/w'.(ParallelTesting::token() ?: '0'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Points the `files` disk at that root, configured the way
|
||||
* config/filesystems.php configures it for the given flag.
|
||||
*
|
||||
* The configuration is *read from that file*, with only the root replaced.
|
||||
* Restating its branch here — which is what this used to do, verbatim down
|
||||
* to the 0755 — meant the test went on passing against its own copy after
|
||||
* somebody changed the shipped one, which is the single thing it exists to
|
||||
* notice.
|
||||
*/
|
||||
function filesDiskWith(bool $webServerReadable): string
|
||||
{
|
||||
$root = storage_path('app/files-permission-test');
|
||||
$root = filesPermissionRoot();
|
||||
|
||||
config(['filesystems.disks.files' => [
|
||||
'driver' => 'local',
|
||||
'root' => $root,
|
||||
'serve' => false,
|
||||
'throw' => false,
|
||||
...($webServerReadable
|
||||
? ['visibility' => 'public', 'permissions' => ['dir' => ['private' => 0755]]]
|
||||
: []),
|
||||
]]);
|
||||
Env::getRepository()->set('FILES_WEB_SERVER_READABLE', $webServerReadable ? 'true' : 'false');
|
||||
|
||||
// Required rather than read through config(), so the env() call in it
|
||||
// is evaluated now, against the flag just set.
|
||||
$shipped = require base_path('config/filesystems.php');
|
||||
|
||||
config(['filesystems.disks.files' => [...$shipped['disks']['files'], 'root' => $root]]);
|
||||
|
||||
Storage::forgetDisk('files');
|
||||
|
||||
@@ -50,11 +70,16 @@ function modeOf(string $path): string
|
||||
|
||||
beforeEach(function () {
|
||||
$this->originalUmask = umask();
|
||||
|
||||
// Also before: a run killed mid-test leaves its tree behind, and these
|
||||
// cases read modes off directories they expect to have created.
|
||||
File::deleteDirectory(filesPermissionRoot());
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
umask($this->originalUmask);
|
||||
File::deleteDirectory(storage_path('app/files-permission-test'));
|
||||
File::deleteDirectory(filesPermissionRoot());
|
||||
Env::getRepository()->clear('FILES_WEB_SERVER_READABLE');
|
||||
Storage::forgetDisk('files');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user