mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
fix: harden workspace skill discovery
This commit is contained in:
@@ -353,17 +353,24 @@ class SkillManager:
|
||||
if not workspace_root:
|
||||
return []
|
||||
|
||||
skills_root = Path(workspace_root) / WORKSPACE_SKILLS_ROOT
|
||||
raw_workspace_root = Path(workspace_root)
|
||||
skills_root = raw_workspace_root / WORKSPACE_SKILLS_ROOT
|
||||
if not skills_root.is_dir():
|
||||
return []
|
||||
|
||||
try:
|
||||
resolved_workspace_root = raw_workspace_root.resolve(strict=True)
|
||||
resolved_skills_root = skills_root.resolve(strict=True)
|
||||
if not resolved_skills_root.is_relative_to(resolved_workspace_root):
|
||||
return []
|
||||
skill_dirs = sorted(
|
||||
resolved_skills_root.iterdir(), key=lambda item: item.name
|
||||
)
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
skills: list[SkillInfo] = []
|
||||
for skill_dir in sorted(skills_root.iterdir(), key=lambda item: item.name):
|
||||
for skill_dir in skill_dirs:
|
||||
if not skill_dir.is_dir():
|
||||
continue
|
||||
skill_name = skill_dir.name
|
||||
|
||||
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from astrbot.core.skills.skill_manager import (
|
||||
SkillInfo,
|
||||
SkillManager,
|
||||
@@ -526,10 +528,7 @@ def test_list_workspace_skills_reads_frontmatter_with_limit(tmp_path: Path):
|
||||
skill_dir = workspace_root / "skills" / "large-skill"
|
||||
skill_dir.mkdir(parents=True)
|
||||
skill_dir.joinpath("SKILL.md").write_text(
|
||||
"---\n"
|
||||
"description: Large workspace skill.\n"
|
||||
"---\n"
|
||||
+ ("x" * (128 * 1024)),
|
||||
"---\ndescription: Large workspace skill.\n---\n" + ("x" * (128 * 1024)),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
@@ -540,6 +539,36 @@ def test_list_workspace_skills_reads_frontmatter_with_limit(tmp_path: Path):
|
||||
assert skills[0].description == "Large workspace skill."
|
||||
|
||||
|
||||
def test_list_workspace_skills_rejects_symlinked_root_outside_workspace(
|
||||
tmp_path: Path,
|
||||
):
|
||||
skills_root = tmp_path / "skills"
|
||||
plugins_root = tmp_path / "plugins"
|
||||
workspace_root = tmp_path / "workspace"
|
||||
external_root = tmp_path / "external-skills"
|
||||
skills_root.mkdir(parents=True, exist_ok=True)
|
||||
plugins_root.mkdir(parents=True, exist_ok=True)
|
||||
workspace_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
external_skill = external_root / "external-skill"
|
||||
external_skill.mkdir(parents=True)
|
||||
external_skill.joinpath("SKILL.md").write_text(
|
||||
"---\ndescription: Outside workspace.\n---\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
try:
|
||||
workspace_root.joinpath("skills").symlink_to(
|
||||
external_root,
|
||||
target_is_directory=True,
|
||||
)
|
||||
except OSError as exc:
|
||||
pytest.skip(f"Directory symlinks are unavailable: {exc}")
|
||||
|
||||
mgr = SkillManager(skills_root=str(skills_root), plugins_root=str(plugins_root))
|
||||
|
||||
assert mgr.list_workspace_skills(workspace_root) == []
|
||||
|
||||
|
||||
def test_list_skills_includes_plugin_provided_skills(monkeypatch, tmp_path: Path):
|
||||
import astrbot.core.star.star as star_module
|
||||
from astrbot.core.star.star import StarMetadata
|
||||
|
||||
Reference in New Issue
Block a user