fix(security): 代码审查 — 修复 9 个安全/稳定性 bug #2

Merged
feibisi merged 2 commits from fix/code-review-security-bugs into main 2026-03-18 16:05:28 +08:00
9 changed files with 173 additions and 36 deletions

View file

@ -414,6 +414,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$source_id = sanitize_text_field( $_POST['source_id'] ?? '' );
@ -423,6 +424,7 @@ class AdminPage {
wp_send_json_success( [ 'message' => __( '状态已更新', 'wpbridge' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '更新失败', 'wpbridge' ) ] );
return;
}
}

@ -434,6 +436,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$source_id = sanitize_text_field( $_POST['source_id'] ?? '' );
@ -441,6 +444,7 @@ class AdminPage {

if ( null === $source ) {
wp_send_json_error( [ 'message' => __( '源不存在', 'wpbridge' ) ] );
return;
}

$checker = new HealthChecker();
@ -461,6 +465,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

\WPBridge\Core\Plugin::clear_all_cache();
@ -476,6 +481,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

try {
@ -484,6 +490,7 @@ class AdminPage {

if ( empty( $api_url ) ) {
wp_send_json_error( [ 'message' => __( '请输入更新源地址', 'wpbridge' ) ] );
return;
}

// 重复源检测
@ -556,12 +563,14 @@ class AdminPage {
$errors = $source->validate();
if ( ! empty( $errors ) ) {
wp_send_json_error( [ 'message' => implode( ', ', $errors ) ] );
return;
}

// 保存
$result = $this->source_manager->add( $source );
if ( ! $result ) {
wp_send_json_error( [ 'message' => __( '保存失败', 'wpbridge' ) ] );
return;
}

wp_send_json_success( [
@ -593,6 +602,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

Logger::clear();
@ -608,6 +618,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$key_name = sanitize_text_field( $_POST['key_name'] ?? '' );
@ -620,6 +631,7 @@ class AdminPage {
$api_key = 'wpb_' . bin2hex( random_bytes( 24 ) );
} catch ( \Exception $e ) {
wp_send_json_error( [ 'message' => __( '生成随机数失败', 'wpbridge' ) ] );
return;
}

// 生成唯一 ID
@ -651,6 +663,7 @@ class AdminPage {
] );
} else {
wp_send_json_error( [ 'message' => __( '生成失败', 'wpbridge' ) ] );
return;
}
}

@ -662,12 +675,14 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$key_id = sanitize_text_field( $_POST['key_id'] ?? '' );

if ( empty( $key_id ) ) {
wp_send_json_error( [ 'message' => __( 'Key ID 不能为空', 'wpbridge' ) ] );
return;
}

// 获取当前 API 设置
@ -687,6 +702,7 @@ class AdminPage {

if ( ! $found ) {
wp_send_json_error( [ 'message' => __( 'Key 不存在', 'wpbridge' ) ] );
return;
}

$api_settings['keys'] = $keys;
@ -696,6 +712,7 @@ class AdminPage {
wp_send_json_success( [ 'message' => __( 'API Key 已撤销', 'wpbridge' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '撤销失败', 'wpbridge' ) ] );
return;
}
}

@ -804,6 +821,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$item_key = sanitize_text_field( $_POST['item_key'] ?? '' );
@ -811,6 +829,7 @@ class AdminPage {

if ( empty( $item_key ) ) {
wp_send_json_error( [ 'message' => __( '项目键不能为空', 'wpbridge' ) ] );
return;
}

$source_registry = new \WPBridge\Core\SourceRegistry();
@ -837,6 +856,7 @@ class AdminPage {
wp_send_json_success( [ 'message' => __( '配置已保存', 'wpbridge' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '保存失败', 'wpbridge' ) ] );
return;
}
}

@ -848,6 +868,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$item_keys = isset( $_POST['item_keys'] ) ? array_map( 'sanitize_text_field', (array) $_POST['item_keys'] ) : [];
@ -856,12 +877,14 @@ class AdminPage {

if ( empty( $item_keys ) ) {
wp_send_json_error( [ 'message' => __( '请选择项目', 'wpbridge' ) ] );
return;
}

// 白名单验证操作类型
$allowed_actions = [ 'set_source', 'reset_default', 'disable' ];
if ( ! in_array( $action, $allowed_actions, true ) ) {
wp_send_json_error( [ 'message' => __( '无效操作', 'wpbridge' ) ] );
return;
}

$source_registry = new \WPBridge\Core\SourceRegistry();
@ -914,6 +937,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$defaults_manager = new \WPBridge\Core\DefaultsManager();
@ -957,6 +981,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$item_key = sanitize_text_field( $_POST['item_key'] ?? '' );
@ -967,11 +992,13 @@ class AdminPage {

if ( empty( $item_key ) || empty( $url ) ) {
wp_send_json_error( [ 'message' => __( '缺少必要参数', 'wpbridge' ) ] );
return;
}

// SSRF 防护:校验 URL 格式 + 禁止内网地址
if ( ! \WPBridge\Security\Validator::is_valid_url( $url ) ) {
wp_send_json_error( [ 'message' => __( '无效的 URL 或不允许访问内网地址', 'wpbridge' ) ] );
return;
}

// "Git 仓库" 选项:根据 URL 自动识别具体平台
@ -1032,6 +1059,7 @@ class AdminPage {

if ( ! $result ) {
wp_send_json_error( [ 'message' => __( '创建更新源失败', 'wpbridge' ) ] );
return;
}

$this->sync_source_to_registry( $source );
@ -1060,6 +1088,7 @@ class AdminPage {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$item_key = sanitize_text_field( $_POST['item_key'] ?? '' );
@ -1067,12 +1096,14 @@ class AdminPage {

if ( empty( $item_key ) ) {
wp_send_json_error( [ 'message' => __( '缺少必要参数', 'wpbridge' ) ] );
return;
}

// 验证模式白名单
$allowed_modes = [ 'default', 'custom', 'disabled' ];
if ( ! in_array( $mode, $allowed_modes, true ) ) {
wp_send_json_error( [ 'message' => __( '无效的模式', 'wpbridge' ) ] );
return;
}

$source_registry = new \WPBridge\Core\SourceRegistry();
@ -1103,11 +1134,13 @@ class AdminPage {

if ( empty( $url ) ) {
wp_send_json_error( [ 'message' => __( '请输入更新地址', 'wpbridge' ) ] );
return;
}

// SSRF 防护:校验 URL 格式 + 禁止内网地址
if ( ! \WPBridge\Security\Validator::is_valid_url( $url ) ) {
wp_send_json_error( [ 'message' => __( '无效的 URL 或不允许访问内网地址', 'wpbridge' ) ] );
return;
}

// 验证源类型白名单
@ -1153,6 +1186,7 @@ class AdminPage {

if ( ! $save_result ) {
wp_send_json_error( [ 'message' => __( '创建更新源失败', 'wpbridge' ) ] );
return;
}

$this->sync_source_to_registry( $source );
@ -1169,6 +1203,7 @@ class AdminPage {
wp_send_json_success( [ 'message' => __( '配置已保存', 'wpbridge' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '保存失败', 'wpbridge' ) ] );
return;
}
}
}

View file

@ -111,6 +111,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$vendor_id = sanitize_key( $_POST['vendor_id'] ?? '' );
@ -123,25 +124,30 @@ class VendorAdmin {
// 验证必填字段
if ( empty( $vendor_id ) ) {
wp_send_json_error( [ 'message' => __( '供应商 ID 不能为空', 'wpbridge' ) ] );
return;
}

if ( empty( $name ) ) {
wp_send_json_error( [ 'message' => __( '供应商名称不能为空', 'wpbridge' ) ] );
return;
}

if ( empty( $api_url ) ) {
wp_send_json_error( [ 'message' => __( 'API 地址不能为空', 'wpbridge' ) ] );
return;
}

// SSRF 防护:校验 URL 格式 + 禁止内网地址
if ( ! \WPBridge\Security\Validator::is_valid_url( $api_url ) ) {
wp_send_json_error( [ 'message' => __( '无效的 API 地址或不允许访问内网地址', 'wpbridge' ) ] );
return;
}

// 验证供应商类型
$allowed_types = [ 'woocommerce', 'wc_am', 'bridge_api' ];
if ( ! in_array( $type, $allowed_types, true ) ) {
wp_send_json_error( [ 'message' => __( '不支持的供应商类型', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->add_vendor(
@ -157,6 +163,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -170,12 +177,14 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$vendor_id = sanitize_key( $_POST['vendor_id'] ?? '' );

if ( empty( $vendor_id ) ) {
wp_send_json_error( [ 'message' => __( '供应商 ID 不能为空', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->remove_vendor( $vendor_id );
@ -184,6 +193,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -197,12 +207,14 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$vendor_id = sanitize_key( $_POST['vendor_id'] ?? '' );

if ( empty( $vendor_id ) ) {
wp_send_json_error( [ 'message' => __( '供应商 ID 不能为空', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->test_vendor_connection( $vendor_id );
@ -211,6 +223,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -224,6 +237,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$vendor_id = sanitize_key( $_POST['vendor_id'] ?? '' );
@ -231,12 +245,14 @@ class VendorAdmin {

if ( empty( $vendor_id ) ) {
wp_send_json_error( [ 'message' => __( '供应商 ID 不能为空', 'wpbridge' ) ] );
return;
}

$vendors = $this->settings->get( 'vendors', [] );

if ( ! isset( $vendors[ $vendor_id ] ) ) {
wp_send_json_error( [ 'message' => __( '供应商不存在', 'wpbridge' ) ] );
return;
}

$vendors[ $vendor_id ]['enabled'] = $enabled;
@ -264,6 +280,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$vendor_id = sanitize_key( $_POST['vendor_id'] ?? '' );
@ -317,6 +334,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$preset_id = sanitize_key( $_POST['preset_id'] ?? '' );
@ -325,19 +343,23 @@ class VendorAdmin {

if ( empty( $preset_id ) ) {
wp_send_json_error( [ 'message' => __( '预设 ID 不能为空', 'wpbridge' ) ] );
return;
}

$preset = PresetRegistry::get_preset( $preset_id );
if ( $preset === null ) {
wp_send_json_error( [ 'message' => __( '无效的预设供应商', 'wpbridge' ) ] );
return;
}

if ( ( $preset['status'] ?? '' ) === 'coming_soon' ) {
wp_send_json_error( [ 'message' => __( '该供应商即将上线,暂不可用', 'wpbridge' ) ] );
return;
}

if ( empty( $email ) || empty( $license ) ) {
wp_send_json_error( [ 'message' => __( '请填写邮箱和授权密钥', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->add_vendor_v2( $preset_id, [
@ -417,17 +439,20 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$preset_id = sanitize_key( $_POST['preset_id'] ?? '' );

if ( empty( $preset_id ) ) {
wp_send_json_error( [ 'message' => __( '预设 ID 不能为空', 'wpbridge' ) ] );
return;
}

$preset = PresetRegistry::get_preset( $preset_id );
if ( $preset === null ) {
wp_send_json_error( [ 'message' => __( '无效的预设供应商', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->remove_vendor( $preset_id );
@ -451,6 +476,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -464,6 +490,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$name = sanitize_text_field( $_POST['name'] ?? '' );
@ -472,16 +499,19 @@ class VendorAdmin {

if ( empty( $api_url ) ) {
wp_send_json_error( [ 'message' => __( 'API 地址不能为空', 'wpbridge' ) ] );
return;
}

if ( empty( $api_key ) ) {
wp_send_json_error( [ 'message' => __( 'API Key 不能为空', 'wpbridge' ) ] );
return;
}

// 验证 URL 协议
$scheme = wp_parse_url( $api_url, PHP_URL_SCHEME );
if ( ! in_array( $scheme, [ 'http', 'https' ], true ) ) {
wp_send_json_error( [ 'message' => __( 'API 地址必须使用 http 或 https 协议', 'wpbridge' ) ] );
return;
}

// 自动生成 vendor_id
@ -508,6 +538,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -521,6 +552,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$plugin_slug = sanitize_key( $_POST['plugin_slug'] ?? '' );
@ -529,6 +561,7 @@ class VendorAdmin {

if ( empty( $plugin_slug ) ) {
wp_send_json_error( [ 'message' => __( '插件 slug 不能为空', 'wpbridge' ) ] );
return;
}

$info = [
@ -542,6 +575,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -555,12 +589,14 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$plugin_slug = sanitize_key( $_POST['plugin_slug'] ?? '' );

if ( empty( $plugin_slug ) ) {
wp_send_json_error( [ 'message' => __( '插件 slug 不能为空', 'wpbridge' ) ] );
return;
}

$result = $this->get_bridge_manager()->remove_custom_plugin( $plugin_slug );
@ -569,6 +605,7 @@ class VendorAdmin {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
return;
}
}

@ -582,12 +619,14 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$sub_manager = $this->get_bridge_manager()->get_subscription_manager();

if ( ! $sub_manager ) {
wp_send_json_error( [ 'message' => __( '订阅管理器未初始化', 'wpbridge' ) ] );
return;
}

$subscription = $sub_manager->get_subscription();
@ -608,12 +647,14 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$sub_manager = $this->get_bridge_manager()->get_subscription_manager();

if ( ! $sub_manager ) {
wp_send_json_error( [ 'message' => __( '订阅管理器未初始化', 'wpbridge' ) ] );
return;
}

$sub_manager->clear_cache();
@ -640,18 +681,21 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$bridge_client = $this->get_bridge_manager()->get_bridge_client();

if ( ! $bridge_client ) {
wp_send_json_error( [ 'message' => __( 'Bridge Server 未配置', 'wpbridge' ) ] );
return;
}

if ( $bridge_client->health_check() ) {
wp_send_json_success( [ 'message' => __( '连接成功', 'wpbridge' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '连接失败', 'wpbridge' ) ] );
return;
}
}

@ -665,6 +709,7 @@ class VendorAdmin {

if ( ! current_user_can( 'install_plugins' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

// 商业包体较大,延长执行时间
@ -675,6 +720,7 @@ class VendorAdmin {

if ( empty( $plugin_slug ) ) {
wp_send_json_error( [ 'message' => __( '插件 slug 不能为空', 'wpbridge' ) ] );
return;
}

$vendor_manager = $this->get_bridge_manager()->get_vendor_manager();
@ -682,6 +728,7 @@ class VendorAdmin {

if ( empty( $download_url ) ) {
wp_send_json_error( [ 'message' => __( '无法获取下载地址,请检查授权是否有效', 'wpbridge' ) ] );
return;
}

require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
@ -694,6 +741,7 @@ class VendorAdmin {
$tmpfile = download_url( $download_url, 600 );
if ( is_wp_error( $tmpfile ) ) {
wp_send_json_error( [ 'message' => __( '下载失败:', 'wpbridge' ) . $tmpfile->get_error_message() ] );
return;
}

// 检查 zip 内容判断是插件还是主题
@ -704,6 +752,7 @@ class VendorAdmin {
if ( ! current_user_can( 'install_themes' ) ) {
@unlink( $tmpfile );
wp_send_json_error( [ 'message' => __( '没有安装主题的权限', 'wpbridge' ) ] );
return;
}
$upgrader = new \Theme_Upgrader( $skin );
} else {
@ -720,12 +769,14 @@ class VendorAdmin {

if ( is_wp_error( $result ) ) {
wp_send_json_error( [ 'message' => $result->get_error_message() ] );
return;
}

if ( $result === false ) {
$errors = $skin->get_errors();
$msg = is_wp_error( $errors ) ? $errors->get_error_message() : __( '安装失败', 'wpbridge' );
wp_send_json_error( [ 'message' => $msg ] );
return;
}

$installed_file = $is_theme ? $upgrader->theme_info() : $upgrader->plugin_info();
@ -850,6 +901,7 @@ class VendorAdmin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpbridge' ) ] );
return;
}

$slug = sanitize_text_field( wp_unslash( $_POST['plugin_slug'] ?? '' ) );
@ -859,6 +911,7 @@ class VendorAdmin {

if ( empty( $slug ) || empty( $vendor_id ) ) {
wp_send_json_error( [ 'message' => __( '参数不完整', 'wpbridge' ) ] );
return;
}

// 根据类型构建 item_key
@ -876,6 +929,7 @@ class VendorAdmin {
}
if ( empty( $found_slug ) ) {
wp_send_json_error( [ 'message' => __( '未找到已安装的主题', 'wpbridge' ) ] );
return;
}
$slug = $found_slug;
}
@ -885,6 +939,7 @@ class VendorAdmin {
$plugin_file = $this->resolve_plugin_file( $slug );
if ( empty( $plugin_file ) ) {
wp_send_json_error( [ 'message' => __( '未找到已安装的插件', 'wpbridge' ) ] );
return;
}
$item_key = 'plugin:' . $plugin_file;
}
@ -898,6 +953,7 @@ class VendorAdmin {
// 确保源存在
if ( ! $source_registry->get( $source_key ) ) {
wp_send_json_error( [ 'message' => __( '供应商更新源不存在,请先激活供应商', 'wpbridge' ) ] );
return;
}
$result = $item_manager->set_source( $item_key, $source_key, 50 );
} else {
@ -912,6 +968,7 @@ class VendorAdmin {
] );
} else {
wp_send_json_error( [ 'message' => __( '操作失败', 'wpbridge' ) ] );
return;
}
}


View file

@ -101,6 +101,9 @@ class BridgeClient {
* @return string|null
*/
public function get_download_url( string $slug ): ?string {
if ( empty( $this->server_url ) ) {
return null;
}
// 下载端点会返回重定向或直接代理
return $this->server_url . "/api/v1/download/{$slug}";
}

View file

@ -72,7 +72,7 @@ class BridgeManager {
public function __construct( Settings $settings, RemoteConfig $remote_config ) {
$this->settings = $settings;
$this->remote_config = $remote_config;
$this->vendor_manager = new VendorManager( $settings );
$this->vendor_manager = VendorManager::get_instance( $settings );

// 初始化 Bridge Server 客户端
$this->init_bridge_client();
@ -310,17 +310,7 @@ class BridgeManager {

$plugin_info = $all_available[ $plugin_slug ];

// 3. 获取下载地址

if ( $gpl_result['is_gpl'] === null && $gpl_result['confidence'] < 50 ) {
// 无法确定,但置信度低,警告用户
Logger::info( 'GPL validation uncertain', [
'plugin' => $plugin_slug,
'result' => $gpl_result,
] );
}

// 3. 检查订阅限制
// 2. 检查订阅限制
$limit_check = $this->check_subscription_limit();
if ( ! $limit_check['allowed'] ) {
return [
@ -337,18 +327,16 @@ class BridgeManager {
$this->settings->set( 'bridged_plugins', $bridged );

Logger::info( 'Plugin bridge enabled', [
'plugin' => $plugin_slug,
'gpl_result' => $gpl_result,
'plugin' => $plugin_slug,
] );
}

return [
'success' => true,
'message' => __( '桥接已启用', 'wpbridge' ),
'code' => 'enabled',
'gpl_result' => $gpl_result,
'source' => $plugin_info['source'] ?? 'official',
'vendor' => $plugin_info['vendor'] ?? null,
'success' => true,
'message' => __( '桥接已启用', 'wpbridge' ),
'code' => 'enabled',
'source' => $plugin_info['source'] ?? 'official',
'vendor' => $plugin_info['vendor'] ?? null,
];
}


View file

@ -147,7 +147,7 @@ class SubscriptionManager {

$response = $vendor->wc_am_product_list();

if ( empty( $response['success'] ) ) {
if ( ! is_array( $response ) || empty( $response['success'] ) ) {
Logger::warning( 'Subscription check failed', [
'vendor' => self::SUBSCRIPTION_VENDOR_ID,
'response' => $response,

View file

@ -433,6 +433,40 @@ class BackupManager {
return new \WP_Error( 'zip_open_failed', __( '无法打开备份文件', 'wpbridge' ) );
}

// Zip Slip 防护:检查所有条目是否在目标目录内
$real_target = realpath( $target_dir );
if ( false === $real_target ) {
$zip->close();
return new \WP_Error( 'invalid_target', __( '目标目录不存在', 'wpbridge' ) );
}
$real_target = rtrim( $real_target, '/' ) . '/';

for ( $i = 0; $i < $zip->numFiles; $i++ ) {
$entry = $zip->getNameIndex( $i );
// 规范化路径并检查是否包含路径遍历
$full_path = realpath( $target_dir ) . '/' . $entry;
$resolved = realpath( dirname( $target_dir . '/' . $entry ) );

// 如果目录尚不存在,用字符串检查
if ( false === $resolved ) {
// 检查原始条目名是否包含 .. 遍历
$normalized = str_replace( '\\', '/', $entry );
if ( strpos( $normalized, '../' ) !== false || strpos( $normalized, '..' . DIRECTORY_SEPARATOR ) !== false ) {
$zip->close();
return new \WP_Error(
'invalid_backup_archive',
sprintf( __( '备份文件包含非法路径: %s', 'wpbridge' ), $entry )
);
}
} elseif ( strpos( $resolved . '/', $real_target ) !== 0 ) {
$zip->close();
return new \WP_Error(
'invalid_backup_archive',
sprintf( __( '备份文件包含非法路径: %s', 'wpbridge' ), $entry )
);
}
}

$zip->extractTo( $target_dir );
$zip->close();


View file

@ -12,6 +12,7 @@ use WPBridge\UpdateSource\ThemeUpdater;
use WPBridge\Admin\AdminPage;
use WPBridge\Admin\VendorAdmin;
use WPBridge\Commercial\CommercialManager;
use WPBridge\Commercial\AutoMatcher;
use WPBridge\API\RestController;

// 防止直接访问
@ -87,6 +88,13 @@ class Plugin {
*/
private ?RestController $rest_controller = null;

/**
* 自动匹配器
*
* @var AutoMatcher|null
*/
private ?AutoMatcher $auto_matcher = null;

/**
* 获取单例实例
*
@ -234,6 +242,10 @@ class Plugin {

// 初始化 Site Health 集成
new SiteHealth( $this->settings );

// 初始化供应商产品自动匹配
$this->auto_matcher = new AutoMatcher();
$this->auto_matcher->init();
}

/**
@ -445,6 +457,7 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

$include_secrets = isset( $_POST['include_secrets'] ) && 'true' === $_POST['include_secrets'];
@ -466,16 +479,19 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

if ( empty( $_POST['config'] ) ) {
wp_send_json_error( array( 'message' => __( '配置数据为空', 'wpbridge' ) ) );
return;
}

$config = json_decode( wp_unslash( $_POST['config'] ), true );

if ( json_last_error() !== JSON_ERROR_NONE ) {
wp_send_json_error( array( 'message' => __( 'JSON 格式无效', 'wpbridge' ) ) );
return;
}

$merge = isset( $_POST['merge'] ) && 'true' === $_POST['merge'];
@ -511,6 +527,7 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

$item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( wp_unslash( $_POST['item_key'] ) ) : '';
@ -519,6 +536,7 @@ class Plugin {

if ( empty( $item_key ) || empty( $lock_type ) ) {
wp_send_json_error( array( 'message' => __( '参数不完整', 'wpbridge' ) ) );
return;
}

$version_lock = VersionLock::get_instance();
@ -534,6 +552,7 @@ class Plugin {
) );
} else {
wp_send_json_error( array( 'message' => __( '锁定失败', 'wpbridge' ) ) );
return;
}
}

@ -545,12 +564,14 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

$item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( wp_unslash( $_POST['item_key'] ) ) : '';

if ( empty( $item_key ) ) {
wp_send_json_error( array( 'message' => __( '参数不完整', 'wpbridge' ) ) );
return;
}

$version_lock = VersionLock::get_instance();
@ -565,6 +586,7 @@ class Plugin {
) );
} else {
wp_send_json_error( array( 'message' => __( '解锁失败', 'wpbridge' ) ) );
return;
}
}

@ -576,6 +598,7 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

$item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( wp_unslash( $_POST['item_key'] ) ) : '';
@ -583,6 +606,7 @@ class Plugin {

if ( empty( $item_key ) || empty( $backup_id ) ) {
wp_send_json_error( array( 'message' => __( '参数不完整', 'wpbridge' ) ) );
return;
}

$backup_manager = BackupManager::get_instance();
@ -590,6 +614,7 @@ class Plugin {

if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
return;
}

wp_send_json_success( array(
@ -605,12 +630,14 @@ class Plugin {

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( '权限不足', 'wpbridge' ) ) );
return;
}

$item_key = isset( $_POST['item_key'] ) ? sanitize_text_field( wp_unslash( $_POST['item_key'] ) ) : '';

if ( empty( $item_key ) ) {
wp_send_json_error( array( 'message' => __( '参数不完整', 'wpbridge' ) ) );
return;
}

$backup_manager = BackupManager::get_instance();

View file

@ -78,14 +78,9 @@ class GiteeHandler extends AbstractHandler {
return null;
}

// 构建 URL带 access_token 参数)
// 构建 URLtoken 通过 Authorization header 发送,避免 URL 泄露)
$url = self::API_BASE . '/repos/' . $repo . '/releases/latest';

$token = $this->get_auth_token();
if ( ! empty( $token ) ) {
$url = add_query_arg( 'access_token', $token, $url );
}

$data = $this->request( $url );

if ( null === $data ) {
@ -150,19 +145,12 @@ class GiteeHandler extends AbstractHandler {
return null;
}

// 获取仓库信息
// 获取仓库信息token 通过 Authorization header 发送)
$repo_url = self::API_BASE . '/repos/' . $repo;
$token = $this->get_auth_token();
if ( ! empty( $token ) ) {
$repo_url = add_query_arg( 'access_token', $token, $repo_url );
}
$repo_data = $this->request( $repo_url );

// 获取最新 Release
$release_url = self::API_BASE . '/repos/' . $repo . '/releases/latest';
if ( ! empty( $token ) ) {
$release_url = add_query_arg( 'access_token', $token, $release_url );
}
$release_data = $this->request( $release_url );

if ( null === $repo_data ) {

View file

@ -151,7 +151,12 @@ class SourceResolver {
if ( ! empty( $secret_ref ) ) {
$secret = get_option( 'wpbridge_secret_' . $secret_ref, '' );
if ( ! empty( $secret ) ) {
$model->auth_token = Encryption::encrypt( $secret );
// 避免双重加密:如果已经是加密数据则直接使用
if ( Encryption::is_encrypted( $secret ) ) {
$model->auth_token = $secret;
} else {
$model->auth_token = Encryption::encrypt( $secret );
}
}
}