diff --git a/app/admin/controller/Ajax.php b/app/admin/controller/Ajax.php
index a6a20be..694d8c9 100644
--- a/app/admin/controller/Ajax.php
+++ b/app/admin/controller/Ajax.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace app\admin\controller;
-use ba\Terminal;
use ba\TableManager;
use support\think\Db;
use app\admin\model\AdminLog;
@@ -166,18 +165,6 @@ class Ajax extends Backend
]);
}
- public function changeTerminalConfig(Request $request): Response
- {
- $response = $this->initializeBackend($request);
- if ($response !== null) return $response;
-
- AdminLog::instance($request)->setTitle(__('Change terminal config'));
- if (Terminal::changeTerminalConfig()) {
- return $this->success();
- }
- return $this->error(__('Failed to modify the terminal configuration. Please modify the configuration file manually:%s', ['/config/terminal.php']));
- }
-
public function clearCache(Request $request): Response
{
$response = $this->initializeBackend($request);
@@ -193,13 +180,4 @@ class Ajax extends Backend
event_trigger('cacheClearAfter');
return $this->success(__('Cache cleaned~'));
}
-
- public function terminal(Request $request): Response
- {
- $response = $this->initializeBackend($request);
- if ($response !== null) return $response;
-
- (new Terminal())->exec();
- return $this->success();
- }
}
diff --git a/app/admin/controller/Index.php b/app/admin/controller/Index.php
index da42fc4..0a83d8b 100644
--- a/app/admin/controller/Index.php
+++ b/app/admin/controller/Index.php
@@ -46,14 +46,10 @@ class Index extends Backend
'siteName' => get_sys_config('site_name'),
'version' => get_sys_config('version'),
'apiUrl' => $apiUrl,
- 'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
+ 'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types', 'forbidden_suffixes']),
'cdnUrl' => full_url(),
'cdnUrlParams' => config('buildadmin.cdn_url_params'),
],
- 'terminal' => [
- 'phpDevelopmentServer' => str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Development Server'),
- 'npmPackageManager' => config('terminal.npm_package_manager'),
- ]
]);
}
diff --git a/app/admin/lang/en.php b/app/admin/lang/en.php
index 7686f9d..6b55612 100644
--- a/app/admin/lang/en.php
+++ b/app/admin/lang/en.php
@@ -30,6 +30,7 @@ return [
'File uploaded successfully' => 'File uploaded successfully',
'No files were uploaded' => 'No files were uploaded',
'The uploaded file format is not allowed' => 'The uploaded file format is no allowance.',
+ 'The uploaded file contains prohibited content' => 'The uploaded file contains prohibited content',
'The uploaded image file is not a valid image' => 'The uploaded image file is not a valid image',
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'The uploaded file is too large (%sMiB), maximum file size:%sMiB',
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'No files have been uploaded or the file size exceeds the server upload limit.',
diff --git a/app/admin/lang/zh-cn.php b/app/admin/lang/zh-cn.php
index b89fb95..d6b9961 100644
--- a/app/admin/lang/zh-cn.php
+++ b/app/admin/lang/zh-cn.php
@@ -31,6 +31,7 @@ return [
'File uploaded successfully' => '文件上传成功!',
'No files were uploaded' => '没有文件被上传',
'The uploaded file format is not allowed' => '上传的文件格式未被允许',
+ 'The uploaded file contains prohibited content' => '上传的文件包含禁止的内容',
'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
diff --git a/app/api/controller/Index.php b/app/api/controller/Index.php
index 1369d79..e8fcdb7 100644
--- a/app/api/controller/Index.php
+++ b/app/api/controller/Index.php
@@ -58,7 +58,7 @@ class Index extends Frontend
'siteName' => get_sys_config('site_name'),
'version' => get_sys_config('version'),
'cdnUrl' => full_url(),
- 'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
+ 'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types', 'forbidden_suffixes']),
'recordNumber' => get_sys_config('record_number'),
'cdnUrlParams' => config('buildadmin.cdn_url_params'),
],
diff --git a/app/api/lang/en.php b/app/api/lang/en.php
index 506db48..53a5fed 100644
--- a/app/api/lang/en.php
+++ b/app/api/lang/en.php
@@ -21,6 +21,7 @@ return [
'File uploaded successfully' => 'File uploaded successfully!',
'No files were uploaded' => 'No files were uploaded',
'The uploaded file format is not allowed' => 'The uploaded file format is not allowed',
+ 'The uploaded file contains prohibited content' => 'The uploaded file contains prohibited content',
'The uploaded image file is not a valid image' => 'The uploaded image is not valid',
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'The file is too large (%sMiB), maximum: %sMiB',
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'No file uploaded or size exceeds server limit',
diff --git a/app/api/lang/ms.php b/app/api/lang/ms.php
index b864e61..a726638 100644
--- a/app/api/lang/ms.php
+++ b/app/api/lang/ms.php
@@ -22,6 +22,7 @@ return [
'File uploaded successfully' => 'Fail berjaya dimuat naik!',
'No files were uploaded' => 'Tiada fail dimuat naik',
'The uploaded file format is not allowed' => 'Format fail yang dimuat naik tidak dibenarkan',
+ 'The uploaded file contains prohibited content' => 'Fail yang dimuat naik mengandungi kandungan yang dilarang',
'The uploaded image file is not a valid image' => 'Fail imej yang dimuat naik tidak sah',
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'Fail terlalu besar (%sM), saiz maksimum: %sM',
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'Tiada fail dimuat naik atau saiz melebihi had pelayan',
diff --git a/app/api/lang/zh-cn.php b/app/api/lang/zh-cn.php
index f596e7e..b35bbf6 100644
--- a/app/api/lang/zh-cn.php
+++ b/app/api/lang/zh-cn.php
@@ -20,6 +20,7 @@ return [
'File uploaded successfully' => '文件上传成功!',
'No files were uploaded' => '没有文件被上传',
'The uploaded file format is not allowed' => '上传的文件格式未被允许',
+ 'The uploaded file contains prohibited content' => '上传的文件包含禁止的内容',
'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
diff --git a/app/common/library/Upload.php b/app/common/library/Upload.php
index fd1df72..3f048d5 100644
--- a/app/common/library/Upload.php
+++ b/app/common/library/Upload.php
@@ -59,6 +59,13 @@ class Upload
public function setDriver(string $driver): self
{
+ if (!preg_match('/^[a-zA-Z0-9_-]+$/', $driver)) {
+ throw new InvalidArgumentException(__('Driver %s not supported', [$driver]));
+ }
+ $allowed = $this->checkConfig($this->config['allowed_drivers'] ?? 'local');
+ if ($allowed && !in_array(strtolower($driver), $allowed)) {
+ throw new InvalidArgumentException(__('Driver %s not supported', [$driver]));
+ }
$this->driver['name'] = $driver;
return $this;
}
@@ -79,11 +86,16 @@ class Upload
protected function resolveDriverClass(string $driver): string|false
{
- if ($this->driver['namespace'] || str_contains($driver, '\\')) {
- $class = str_contains($driver, '\\') ? $driver : $this->driver['namespace'] . $this->studly($driver);
- if (class_exists($class)) {
- return $class;
- }
+ if (!preg_match('/^[a-zA-Z0-9_-]+$/', $driver)) {
+ return false;
+ }
+ $allowed = $this->checkConfig($this->config['allowed_drivers'] ?? 'local');
+ if ($allowed && !in_array(strtolower($driver), $allowed)) {
+ return false;
+ }
+ $class = $this->driver['namespace'] . $this->studly($driver);
+ if (class_exists($class)) {
+ return $class;
}
return false;
}
@@ -181,6 +193,10 @@ class Upload
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
+ $this->assertSafeFileName($this->fileInfo['name']);
+ $this->assertNoMaliciousContent();
+ $this->assertImageMime();
+
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $this->topic)) {
throw new RuntimeException(__('Topic format error'));
}
@@ -257,4 +273,95 @@ class Upload
}
return $configItem ? explode(',', strtolower((string)$configItem)) : [];
}
+
+ protected function getForbiddenSuffixes(): array
+ {
+ return $this->checkConfig($this->config['forbidden_suffixes'] ?? '');
+ }
+
+ protected function assertSafeFileName(string $fileName): void
+ {
+ $forbidden = $this->getForbiddenSuffixes();
+ if (!$forbidden) {
+ return;
+ }
+
+ $parts = explode('.', strtolower($fileName));
+ if (count($parts) < 2) {
+ return;
+ }
+
+ foreach ($parts as $index => $part) {
+ if ($part === '') {
+ throw new RuntimeException(__('The uploaded file format is not allowed'));
+ }
+ if ($index === count($parts) - 1) {
+ continue;
+ }
+ if (in_array($part, $forbidden)) {
+ throw new RuntimeException(__('The uploaded file format is not allowed'));
+ }
+ }
+
+ $suffix = $parts[count($parts) - 1];
+ if (in_array($suffix, $forbidden)) {
+ throw new RuntimeException(__('The uploaded file format is not allowed'));
+ }
+ }
+
+ protected function assertNoMaliciousContent(): void
+ {
+ $path = $this->file->getPathname();
+ if (!is_file($path) || !is_readable($path)) {
+ return;
+ }
+
+ $handle = fopen($path, 'rb');
+ if (!$handle) {
+ throw new RuntimeException(__('The uploaded file format is not allowed'));
+ }
+ $chunk = fread($handle, 8192);
+ fclose($handle);
+ if ($chunk === false || $chunk === '') {
+ return;
+ }
+
+ $patterns = [
+ '/<\?php/i',
+ '/<\?=/i',
+ '/<\?(?!xml)/i',
+ '/<%[@=]?/i',
+ '/
-
-
diff --git a/web/src/layouts/backend/components/navMenus.vue b/web/src/layouts/backend/components/navMenus.vue
index 78d5cda..b2a33f9 100644
--- a/web/src/layouts/backend/components/navMenus.vue
+++ b/web/src/layouts/backend/components/navMenus.vue
@@ -71,13 +71,6 @@