This commit is contained in:
hashbro
2026-08-24 06:49:07 +08:00
parent db574cc629
commit 2eb081cbb8
18 changed files with 436 additions and 40 deletions
+45 -11
View File
@@ -159,7 +159,8 @@ class PhotoPreview
private function convertCommands(string $src, string $dst): array
{
$cmds = [];
if (is_executable('/usr/bin/sips')) {
// sips is macOS-only. Probing /usr/bin/sips fatals under panel open_basedir.
if (PHP_OS_FAMILY === 'Darwin' && $this->isSafeExecutable('/usr/bin/sips')) {
$cmds[] = ['/usr/bin/sips', '-s', 'format', 'jpeg', '--out', $dst, $src];
}
foreach (['heif-convert', 'magick'] as $bin) {
@@ -178,25 +179,28 @@ class PhotoPreview
private function resolveBinary(string $name): ?string
{
$candidates = match ($name) {
'magick' => ['magick', '/opt/homebrew/bin/magick', '/usr/local/bin/magick', '/usr/bin/magick'],
'heif-convert' => ['heif-convert', '/opt/homebrew/bin/heif-convert', '/usr/local/bin/heif-convert', '/usr/bin/heif-convert'],
'magick' => [base_path('bin/magick'), 'magick', '/opt/homebrew/bin/magick', '/usr/local/bin/magick', '/usr/bin/magick'],
'heif-convert' => [base_path('bin/heif-convert'), 'heif-convert', '/opt/homebrew/bin/heif-convert', '/usr/local/bin/heif-convert', '/usr/bin/heif-convert'],
default => [$name],
};
$bare = null;
foreach ($candidates as $bin) {
if (str_contains($bin, DIRECTORY_SEPARATOR)) {
if (is_executable($bin)) {
return $bin;
if (! str_contains($bin, DIRECTORY_SEPARATOR)) {
$found = $this->which($bin);
if ($found !== null) {
return $found;
}
$bare ??= $bin;
continue;
}
$found = $this->which($bin);
if ($found !== null) {
return $found;
if ($this->isSafeExecutable($bin)) {
return $bin;
}
}
return null;
// exec() is often allowed when is_executable() is not; let Process try PATH.
return $bare;
}
private function which(string $name): ?string
@@ -207,7 +211,7 @@ class PhotoPreview
}
foreach (explode(PATH_SEPARATOR, $path) as $dir) {
$candidate = rtrim($dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
if (is_executable($candidate)) {
if ($this->isSafeExecutable($candidate)) {
return $candidate;
}
}
@@ -215,6 +219,36 @@ class PhotoPreview
return null;
}
private function isSafeExecutable(string $path): bool
{
if (! $this->isPathInsideOpenBasedir($path)) {
return false;
}
return @is_file($path) && @is_executable($path);
}
private function isPathInsideOpenBasedir(string $path): bool
{
$basedir = (string) ini_get('open_basedir');
if ($basedir === '') {
return true;
}
$real = realpath($path);
$check = $real !== false ? $real : $path;
foreach (explode(PATH_SEPARATOR, $basedir) as $root) {
$root = rtrim($root, DIRECTORY_SEPARATOR);
if ($root === '') {
continue;
}
if ($check === $root || str_starts_with($check, $root.DIRECTORY_SEPARATOR)) {
return true;
}
}
return false;
}
private function cachePath(string $deviceKey, string $sha256): string
{
$sha = preg_replace('/[^0-9a-fA-F]/', '', $sha256) ?? '';