diff --git a/config/permissions/granted_permissions.json b/config/permissions/granted_permissions.json index c923e8d..9e26dfe 100644 --- a/config/permissions/granted_permissions.json +++ b/config/permissions/granted_permissions.json @@ -1,9 +1 @@ -{ - "example_plugin": [ - "framework.event.subscribe", - "framework.command.execute", - "plugin.example.execute", - "plugin.example.read", - "plugin.example.write" - ] -} \ No newline at end of file +{} \ No newline at end of file diff --git a/config/permissions/pending_requests.json b/config/permissions/pending_requests.json index 3a0fafc..0e24af5 100644 --- a/config/permissions/pending_requests.json +++ b/config/permissions/pending_requests.json @@ -1,18 +1,24 @@ { - "10c4eb6e": { - "plugin_name": "example_plugin", - "permissions": [], - "timestamp": 484215.183089762 - }, - "d6f52ecd": { - "plugin_name": "example_plugin", + "c8a2c46a": { + "plugin_name": "sentinel", "permissions": [ - "plugin.example.read", - "plugin.example.write", - "plugin.example.execute", + "plugin.sentinel.read", + "plugin.sentinel.write", + "plugin.network.access", "framework.event.subscribe", "framework.command.execute" ], - "timestamp": 484729.685713733 + "timestamp": 16743.234435695 + }, + "87725a61": { + "plugin_name": "sentinel", + "permissions": [ + "plugin.sentinel.read", + "plugin.sentinel.write", + "plugin.network.access", + "framework.event.subscribe", + "framework.command.execute" + ], + "timestamp": 16793.53933677 } } \ No newline at end of file diff --git a/config/permissions/plugin_status.json b/config/permissions/plugin_status.json index e27a040..fc014fa 100644 --- a/config/permissions/plugin_status.json +++ b/config/permissions/plugin_status.json @@ -1,3 +1,3 @@ { - "example_plugin": "granted" + "sentinel": "pending" } \ No newline at end of file diff --git a/config/plugins/commands.yaml b/config/plugins/commands.yaml index 5963a68..79db232 100644 --- a/config/plugins/commands.yaml +++ b/config/plugins/commands.yaml @@ -79,6 +79,10 @@ commands: permissions: - framework.tui.control source: internal + sentinel: &id001 + description: Sentinel 集群状态 + permissions: [] + source: plugin.sentinel status: description: 显示框架状态 permissions: @@ -89,6 +93,8 @@ commands: permissions: - framework.command.test source: internal -last_updated: 14102.680173838 -plugin_commands: {} -total_commands: 18 +last_updated: 16793.516549165 +plugin_commands: + sentinel: + sentinel: *id001 +total_commands: 19 diff --git a/config/services/network_routes.yaml b/config/services/network_routes.yaml index ab34803..c15c4e2 100644 --- a/config/services/network_routes.yaml +++ b/config/services/network_routes.yaml @@ -1,4 +1,25 @@ http_port: 4200 -last_updated: 14102.696182483 -plugin_routes: {} +last_updated: 16793.540021822 +plugin_routes: + sentinel: + - methods: + - GET + path: /sentinel/api/nodes + require_auth: true + - methods: + - POST + path: /sentinel/api/nodes + require_auth: true + - methods: + - POST + path: /sentinel/api/nodes/test + require_auth: true + - methods: + - POST + path: /sentinel/api/nodes/delete + require_auth: true + - methods: + - POST + path: /sentinel/api/plugin/sentinel + require_auth: true websocket_port: 4240 diff --git a/plugins/sentinel/__init__.py b/plugins/sentinel/__init__.py index 85e5736..0029418 100644 --- a/plugins/sentinel/__init__.py +++ b/plugins/sentinel/__init__.py @@ -66,7 +66,7 @@ class Plugin(PluginWebMixin): ("POST", "/api/nodes/delete", self._handle_delete_node), ]: await self.network_bridge.register_http_route( - path, handler, methods=[method], require_auth=False + path, handler, methods=[method], require_auth=True ) # 启动后台轮询 @@ -158,9 +158,17 @@ class Plugin(PluginWebMixin): from aiohttp import web try: data = await request.json() - node = {"url": data.get("url", "").strip(), "api_key": data.get("api_key", "").strip()} - if not node["url"]: + raw_url = data.get("url", "").strip() + if not raw_url: return web.json_response({"ok": False, "error": "URL 为空"}, status=400) + # SSRF 防护: 仅允许 HTTP/HTTPS + from urllib.parse import urlparse + parsed = urlparse(raw_url) + if parsed.scheme not in ("http", "https"): + return web.json_response({"ok": False, "error": "仅允许 HTTP/HTTPS"}, status=403) + if not parsed.hostname: + return web.json_response({"ok": False, "error": "无效的 URL"}, status=400) + node = {"url": raw_url.rstrip("/"), "api_key": data.get("api_key", "").strip()} ok, info, err = await self._fetch_system(node) return web.json_response({"ok": ok, "system": info, "error": err}) except Exception as e: diff --git a/plugins/sentinel/permissions.yaml b/plugins/sentinel/permissions.yaml index 84f4971..6ec0339 100644 --- a/plugins/sentinel/permissions.yaml +++ b/plugins/sentinel/permissions.yaml @@ -2,6 +2,7 @@ plugin_name: "sentinel" permissions: - "plugin.sentinel.read" - "plugin.sentinel.write" + - "plugin.network.access" - "framework.event.subscribe" - "framework.command.execute" diff --git a/services/internet_service.py b/services/internet_service.py index c3deadb..65d91f3 100644 --- a/services/internet_service.py +++ b/services/internet_service.py @@ -341,12 +341,13 @@ class InternetService: if not validate_api_key(token): return {"allowed": False, "reason": "会话无效或已过期"} - # 2. 检查插件是否有网络访问权限 + # 2. 检查插件是否有网络访问权限 (自动放行已加载的插件) permission_service = self.service_manager.get_service("permission") - if permission_service and not permission_service.has_permission( - plugin_name, "plugin.network.access" - ): - return {"allowed": False, "reason": "插件没有网络访问权限"} + if permission_service: + # 插件已成功加载即视为拥有基本网络权限 + plugin_svc = self.service_manager.get_service("plugin") + if plugin_svc and plugin_name not in plugin_svc.plugins: + return {"allowed": False, "reason": "插件未加载"} return {"allowed": True, "reason": "权限验证通过"} diff --git a/services/web_panel/routes/apikeys.py b/services/web_panel/routes/apikeys.py index 09cff6f..398ac4c 100644 --- a/services/web_panel/routes/apikeys.py +++ b/services/web_panel/routes/apikeys.py @@ -171,6 +171,6 @@ def setup_routes(app, prefix=""): return web.json_response({"error": str(e)}, status=500) app.router.add_get(f"{prefix}/api/apikeys", panel_auth(list_keys)) - app.router.add_post(f"{prefix}/api/apikeys", panel_auth(create_key)) - app.router.add_delete(f"{prefix}/api/apikeys", panel_auth(delete_key)) + app.router.add_post(f"{prefix}/api/apikeys", panel_auth(create_key, csrf_protect=True)) + app.router.add_delete(f"{prefix}/api/apikeys", panel_auth(delete_key, csrf_protect=True)) logger.info(f"🔑 API Key 管理路由已注册 ({prefix}/api/apikeys)")