mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
fix(security): restrict agent log file access (#6050)
This commit is contained in:
@@ -315,8 +315,6 @@ class PromptManager:
|
|||||||
"项目根目录": settings.ROOT_PATH,
|
"项目根目录": settings.ROOT_PATH,
|
||||||
"配置目录": settings.CONFIG_PATH,
|
"配置目录": settings.CONFIG_PATH,
|
||||||
"临时目录": settings.TEMP_PATH,
|
"临时目录": settings.TEMP_PATH,
|
||||||
"日志目录": settings.LOG_PATH,
|
|
||||||
"主日志文件": settings.LOG_PATH / "moviepilot.log",
|
|
||||||
}
|
}
|
||||||
return [f" - {label}: `{path}`" for label, path in paths.items()]
|
return [f" - {label}: `{path}`" for label, path in paths.items()]
|
||||||
|
|
||||||
|
|||||||
@@ -425,7 +425,6 @@ class MoviePilotTool(BaseTool, metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
roots = [
|
roots = [
|
||||||
settings.CONFIG_PATH / "agent",
|
settings.CONFIG_PATH / "agent",
|
||||||
settings.LOG_PATH,
|
|
||||||
]
|
]
|
||||||
resolved_roots = []
|
resolved_roots = []
|
||||||
for root in roots:
|
for root in roots:
|
||||||
@@ -461,7 +460,7 @@ class MoviePilotTool(BaseTool, metaclass=ABCMeta):
|
|||||||
allowed_text = "、".join(str(root) for root in allowed_roots)
|
allowed_text = "、".join(str(root) for root in allowed_roots)
|
||||||
return (
|
return (
|
||||||
resolved_path,
|
resolved_path,
|
||||||
f"抱歉,普通用户只能{operation}Agent配置目录和日志目录内的文件或目录:{allowed_text}",
|
f"抱歉,普通用户只能{operation}Agent配置目录内的文件或目录:{allowed_text}",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _check_local_storage_access(
|
async def _check_local_storage_access(
|
||||||
@@ -483,7 +482,7 @@ class MoviePilotTool(BaseTool, metaclass=ABCMeta):
|
|||||||
return None, None
|
return None, None
|
||||||
return (
|
return (
|
||||||
None,
|
None,
|
||||||
f"抱歉,普通用户只能{operation}本地配置目录、Agent记忆目录和日志目录,不能访问远程存储。",
|
f"抱歉,普通用户只能{operation}本地Agent配置目录,不能访问远程存储。",
|
||||||
)
|
)
|
||||||
|
|
||||||
return await self._check_local_file_access(path=path, operation=operation)
|
return await self._check_local_file_access(path=path, operation=operation)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class EditFileTool(MoviePilotTool):
|
|||||||
description: str = (
|
description: str = (
|
||||||
"Edit a local text file by replacing specific old text with new text. "
|
"Edit a local text file by replacing specific old text with new text. "
|
||||||
"Non-admin users can only edit files inside the MoviePilot Agent config "
|
"Non-admin users can only edit files inside the MoviePilot Agent config "
|
||||||
"and log directories."
|
"directory."
|
||||||
)
|
)
|
||||||
args_schema: Type[BaseModel] = EditFileInput
|
args_schema: Type[BaseModel] = EditFileInput
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class WriteFileTool(MoviePilotTool):
|
|||||||
]
|
]
|
||||||
description: str = (
|
description: str = (
|
||||||
"Write full content to a local text file. Non-admin users can only write "
|
"Write full content to a local text file. Non-admin users can only write "
|
||||||
"inside the MoviePilot Agent config and log directories."
|
"inside the MoviePilot Agent config directory."
|
||||||
)
|
)
|
||||||
args_schema: Type[BaseModel] = WriteFileInput
|
args_schema: Type[BaseModel] = WriteFileInput
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ def test_moviepilot_info_does_not_expose_api_token_or_database_password(monkeypa
|
|||||||
assert "db.example.local" not in moviepilot_info
|
assert "db.example.local" not in moviepilot_info
|
||||||
assert str(settings.CONFIG_PATH) in moviepilot_info
|
assert str(settings.CONFIG_PATH) in moviepilot_info
|
||||||
assert str(settings.TEMP_PATH) in moviepilot_info
|
assert str(settings.TEMP_PATH) in moviepilot_info
|
||||||
assert str(settings.LOG_PATH) in moviepilot_info
|
assert str(settings.LOG_PATH) not in moviepilot_info
|
||||||
assert str(settings.LOG_PATH / "moviepilot.log") in moviepilot_info
|
assert str(settings.LOG_PATH / "moviepilot.log") not in moviepilot_info
|
||||||
assert str(settings.LOG_PATH / "moviepilot.stdout.log") not in moviepilot_info
|
assert str(settings.LOG_PATH / "moviepilot.stdout.log") not in moviepilot_info
|
||||||
assert str(settings.LOG_PATH / "moviepilot.frontend.stdout.log") not in moviepilot_info
|
assert str(settings.LOG_PATH / "moviepilot.frontend.stdout.log") not in moviepilot_info
|
||||||
|
assert "日志目录" not in moviepilot_info
|
||||||
|
assert "主日志文件" not in moviepilot_info
|
||||||
assert str(settings.CONFIG_PATH / "agent") not in moviepilot_info
|
assert str(settings.CONFIG_PATH / "agent") not in moviepilot_info
|
||||||
assert str(settings.CONFIG_PATH / "agent" / "memory") not in moviepilot_info
|
assert str(settings.CONFIG_PATH / "agent" / "memory") not in moviepilot_info
|
||||||
assert str(settings.CONFIG_PATH / "agent" / "skills") not in moviepilot_info
|
assert str(settings.CONFIG_PATH / "agent" / "skills") not in moviepilot_info
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ def test_non_admin_file_tools_can_access_config_directory(tmp_path, monkeypatch)
|
|||||||
def test_non_admin_file_tools_block_paths_outside_allowed_roots(
|
def test_non_admin_file_tools_block_paths_outside_allowed_roots(
|
||||||
tmp_path, monkeypatch
|
tmp_path, monkeypatch
|
||||||
):
|
):
|
||||||
"""普通用户不能通过文件工具访问配置、记忆和日志目录外的路径。"""
|
"""普通用户不能通过文件工具访问 Agent 配置目录外的路径。"""
|
||||||
config_path = tmp_path / "config"
|
config_path = tmp_path / "config"
|
||||||
outside_path = tmp_path / "outside.txt"
|
outside_path = tmp_path / "outside.txt"
|
||||||
outside_path.write_text("secret", encoding="utf-8")
|
outside_path.write_text("secret", encoding="utf-8")
|
||||||
@@ -208,10 +208,47 @@ def test_non_admin_file_tools_block_paths_outside_allowed_roots(
|
|||||||
assert "普通用户只能写入" in write_result
|
assert "普通用户只能写入" in write_result
|
||||||
assert "普通用户只能编辑" in edit_result
|
assert "普通用户只能编辑" in edit_result
|
||||||
assert "普通用户只能列出" in list_result
|
assert "普通用户只能列出" in list_result
|
||||||
|
assert "日志目录" not in read_result
|
||||||
|
assert "日志目录" not in write_result
|
||||||
|
assert "日志目录" not in edit_result
|
||||||
|
assert "日志目录" not in list_result
|
||||||
assert outside_path.read_text(encoding="utf-8") == "secret"
|
assert outside_path.read_text(encoding="utf-8") == "secret"
|
||||||
list_directory.assert_not_called()
|
list_directory.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_non_admin_file_tools_block_log_directory(tmp_path, monkeypatch):
|
||||||
|
"""普通用户不能通过文件工具读写运行日志目录。"""
|
||||||
|
config_path = tmp_path / "config"
|
||||||
|
monkeypatch.setattr(settings, "CONFIG_DIR", str(config_path))
|
||||||
|
log_path = settings.LOG_PATH / "moviepilot.log"
|
||||||
|
log_path.parent.mkdir(parents=True)
|
||||||
|
log_path.write_text("secret log", encoding="utf-8")
|
||||||
|
|
||||||
|
read_tool = ReadFileTool(session_id="session-1", user_id="10001")
|
||||||
|
write_tool = WriteFileTool(session_id="session-1", user_id="10001")
|
||||||
|
edit_tool = EditFileTool(session_id="session-1", user_id="10001")
|
||||||
|
list_tool = ListDirectoryTool(session_id="session-1", user_id="10001")
|
||||||
|
|
||||||
|
read_result = asyncio.run(read_tool.run(str(log_path)))
|
||||||
|
write_result = asyncio.run(write_tool.run(str(log_path), "changed"))
|
||||||
|
edit_result = asyncio.run(edit_tool.run(str(log_path), "secret log", "changed"))
|
||||||
|
with patch.object(
|
||||||
|
ListDirectoryTool, "_list_directory_sync", return_value="listed"
|
||||||
|
) as list_directory:
|
||||||
|
list_result = asyncio.run(list_tool.run(str(settings.LOG_PATH)))
|
||||||
|
|
||||||
|
assert "普通用户只能读取" in read_result
|
||||||
|
assert "普通用户只能写入" in write_result
|
||||||
|
assert "普通用户只能编辑" in edit_result
|
||||||
|
assert "普通用户只能列出" in list_result
|
||||||
|
assert "日志目录" not in read_result
|
||||||
|
assert "日志目录" not in write_result
|
||||||
|
assert "日志目录" not in edit_result
|
||||||
|
assert "日志目录" not in list_result
|
||||||
|
assert log_path.read_text(encoding="utf-8") == "secret log"
|
||||||
|
list_directory.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_admin_file_tool_can_access_paths_outside_allowed_roots(
|
def test_admin_file_tool_can_access_paths_outside_allowed_roots(
|
||||||
tmp_path, monkeypatch
|
tmp_path, monkeypatch
|
||||||
):
|
):
|
||||||
|
|||||||
Reference in New Issue
Block a user