diff --git a/README.md b/README.md index e4c63e1569..5c9af5e69e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

Python 3.12+ License: MIT - Version + Version PyPI Code Style: Ruff GitHub stars diff --git a/README_CN.md b/README_CN.md index bf6d49a47e..f868a23bf8 100644 --- a/README_CN.md +++ b/README_CN.md @@ -15,7 +15,7 @@

Python 3.12+ License: MIT - Version + Version PyPI Code Style: Ruff GitHub stars diff --git a/fnos/docker/app/docker/docker-compose.yaml b/fnos/docker/app/docker/docker-compose.yaml index e7777579ee..ec313a5e2e 100644 --- a/fnos/docker/app/docker/docker-compose.yaml +++ b/fnos/docker/app/docker/docker-compose.yaml @@ -1,6 +1,6 @@ services: octop: - image: ghcr.io/tencentcloud/octop:1.0.2b6 + image: ghcr.io/tencentcloud/octop:1.0.2b7 container_name: octop restart: unless-stopped pull_policy: missing diff --git a/fnos/docker/manifest b/fnos/docker/manifest index 5f8c16888f..21b6ca0f32 100644 --- a/fnos/docker/manifest +++ b/fnos/docker/manifest @@ -1,5 +1,5 @@ appname=octop -version=1.0.2b6 +version=1.0.2b7 display_name=Octop desc=腾讯开源的多用户多智能体 AI 助手平台,支持 Web 控制台、CLI、IM 渠道与定时任务,数据全部本地存储。

安装后使用:
首次会从 ghcr.io 下载与本包版本对应的镜像(设备需能访问 GitHub Container Registry),请等到应用中心显示「运行中」再点「打开」,或访问 http://设备IP:8088,用安装向导里设置的账号登录。
忘记密码可到数据目录 octop-login.txt 查看备份(仅记录安装或应用「设置」改密;网页改密后不会自动更新)。

首次登录后:
请在控制台「设置 → 模型」配置 API Key,并按需配置 IM 渠道、远程桌面、浏览器等。

注意:Docker 版通过容器部署,会拉取 ghcr.io/tencentcloud/octop 上与本包版本相同的镜像。 source=thirdparty diff --git a/fnos/native/manifest b/fnos/native/manifest index 7a8265caf6..f2fe0a538f 100644 --- a/fnos/native/manifest +++ b/fnos/native/manifest @@ -1,5 +1,5 @@ appname=octop-native -version=1.0.2b6 +version=1.0.2b7 display_name=Octop desc=腾讯开源的多用户多智能体 AI 助手平台,支持 Web 控制台、CLI、IM 渠道与定时任务,数据全部本地存储。

安装后使用:
请等到应用中心显示「运行中」再点「打开」,或访问 http://设备IP:8089,用安装向导里设置的用户名或邮箱登录。
忘记密码时打开飞牛「文件管理」,进入应用共享里的 Octop 数据目录,查看 octop-login.txt(仅备份安装或应用「设置」改密;网页改密后不会自动更新);也可在应用「设置」中改密。

命令行管理:
支持官方 octop CLI(octop provider list / agent list / user list / skills / backup 等,详见 octop --help)。

首次登录后:
请在控制台「设置 → 模型」配置 API Key,并按需配置 IM 渠道、远程桌面、浏览器等。

注意:本地版直接运行在飞牛主机上,安装前会自动关联安装「Python 3.12」开发工具。 source=thirdparty diff --git a/src/octop/__init__.py b/src/octop/__init__.py index 847a5c322a..3836b89559 100644 --- a/src/octop/__init__.py +++ b/src/octop/__init__.py @@ -4,7 +4,7 @@ from octop.infra.utils.httpx_proxy import install_httpx_cidr_no_proxy -__version__ = "1.0.2b6" +__version__ = "1.0.2b7" # httpx 0.28 treats NO_PROXY CIDR as an exact IP (see issue #1347). Patch once # at import so CLI, ``octop run``, and in-process harness clients all honor it. diff --git a/src/octop/infra/gateway/media/backend_files.py b/src/octop/infra/gateway/media/backend_files.py index 15f63316fe..5e76369a26 100644 --- a/src/octop/infra/gateway/media/backend_files.py +++ b/src/octop/infra/gateway/media/backend_files.py @@ -145,7 +145,14 @@ def is_allowed_host_temp_path(resolved: Path) -> bool: def file_url_to_abs_path(file_url: str) -> str: parsed = urllib.parse.urlparse(file_url) + netloc = parsed.netloc path = parsed.path + # file://C:/… — two slashes, so urlparse puts the drive letter in netloc + # and path alone loses it. Without this the result is drive-relative + # (D:\Users\… when the workdir is on D:), which both escapes the + # containment check and writes somewhere the caller did not name. + if len(netloc) == 2 and netloc[1] == ":" and netloc[0].isalpha(): + return str(Path(f"{netloc}{urllib.parse.unquote(path)}")) # file:///C:/… — Windows drive in URL path if len(path) >= 3 and path[0] == "/" and path[2] == ":": return str(Path(urllib.parse.unquote(path[1:]))) diff --git a/tests/unit/test_host_download_allow.py b/tests/unit/test_host_download_allow.py index 4add0cf25c..b08cb10daf 100644 --- a/tests/unit/test_host_download_allow.py +++ b/tests/unit/test_host_download_allow.py @@ -66,3 +66,28 @@ def test_file_url_windows_drive_decodes_unicode() -> None: """file:///C:/… must unquote and match native Path form (Windows CI).""" url = "file:///C:/Users/me/out/%E4%BF%9D%E6%8A%A4.pptx" assert file_url_to_abs_path(url) == str(Path("C:/Users/me/out/保护.pptx")) + + +def test_file_url_windows_drive_in_netloc_keeps_drive() -> None: + """``file://C:/…`` — two slashes, so the drive lands in netloc, not path. + + Windows CI builds these URLs from ``Path.as_posix()``, which yields + ``file://C:/…``. ``urlparse`` puts ``C:`` in netloc, so reading ``path`` + alone loses the drive and returns ``/Users/…``. On a runner whose workdir + is on D: that resolves drive-relative, which both escapes the containment + check and writes somewhere the caller did not name — SEC-2's file:// + write test came back 200 instead of 403. + """ + assert file_url_to_abs_path("file://C:/Users/me/out/a.pptx") == str( + Path("C:/Users/me/out/a.pptx") + ) + # Percent-decoding still applies on this branch. + assert file_url_to_abs_path("file://C:/Users/me/out/%E4%BF%9D%E6%8A%A4.pptx") == str( + Path("C:/Users/me/out/保护.pptx") + ) + # The three-slash form is unchanged by the fix. + assert file_url_to_abs_path("file:///C:/Users/me/out/a.pptx") == str( + Path("C:/Users/me/out/a.pptx") + ) + # A UNC host is still a host, not a drive letter. + assert file_url_to_abs_path("file://server/share/a.pptx") != str(Path("C:/share/a.pptx"))