mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-10 17:35:57 +00:00
Fix custom skill install permissions (#3241)
* Fix custom skill install permissions * Fix skill upload test portability * Keep custom skill writes sandbox readable * Clear sandbox write bits on skill permissions * Limit custom skill write permission updates
This commit is contained in:
@@ -13,6 +13,7 @@ import stat
|
||||
import zipfile
|
||||
from pathlib import Path, PurePosixPath, PureWindowsPath
|
||||
|
||||
from deerflow.skills.permissions import make_skill_tree_sandbox_readable
|
||||
from deerflow.skills.security_scanner import scan_skill_content
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -139,6 +140,7 @@ def _move_staged_skill_into_reserved_target(staging_target: Path, target: Path)
|
||||
reserved = True
|
||||
for child in staging_target.iterdir():
|
||||
shutil.move(str(child), target / child.name)
|
||||
make_skill_tree_sandbox_readable(target)
|
||||
installed = True
|
||||
except FileExistsError as e:
|
||||
raise SkillAlreadyExistsError(f"Skill '{target.name}' already exists") from e
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Filesystem permission helpers for installed skill trees."""
|
||||
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def make_skill_path_sandbox_readable(path: Path) -> None:
|
||||
if path.is_symlink():
|
||||
return
|
||||
mode = stat.S_IMODE(path.stat().st_mode)
|
||||
without_sandbox_write = mode & ~(stat.S_IWGRP | stat.S_IWOTH)
|
||||
if path.is_dir():
|
||||
path.chmod(without_sandbox_write | 0o555)
|
||||
elif path.is_file():
|
||||
path.chmod(without_sandbox_write | 0o444)
|
||||
|
||||
|
||||
def make_skill_tree_sandbox_readable(target: Path) -> None:
|
||||
make_skill_path_sandbox_readable(target)
|
||||
for path in target.rglob("*"):
|
||||
make_skill_path_sandbox_readable(path)
|
||||
|
||||
|
||||
def make_skill_written_path_sandbox_readable(skill_root: Path, target: Path) -> None:
|
||||
resolved_root = skill_root.resolve()
|
||||
resolved_target = target.resolve()
|
||||
resolved_target.relative_to(resolved_root)
|
||||
|
||||
make_skill_path_sandbox_readable(resolved_root)
|
||||
current = resolved_root
|
||||
for part in resolved_target.parent.relative_to(resolved_root).parts:
|
||||
current = current / part
|
||||
make_skill_path_sandbox_readable(current)
|
||||
make_skill_path_sandbox_readable(resolved_target)
|
||||
@@ -13,6 +13,7 @@ from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from deerflow.config.runtime_paths import resolve_path
|
||||
from deerflow.skills.permissions import make_skill_written_path_sandbox_readable
|
||||
from deerflow.skills.storage.skill_storage import SKILL_MD_FILE, SkillStorage
|
||||
from deerflow.skills.types import SkillCategory
|
||||
|
||||
@@ -90,6 +91,7 @@ class LocalSkillStorage(SkillStorage):
|
||||
tmp_file.write(content)
|
||||
tmp_path = Path(tmp_file.name)
|
||||
tmp_path.replace(target)
|
||||
make_skill_written_path_sandbox_readable(self.get_custom_skill_dir(name), target)
|
||||
|
||||
async def ainstall_skill_from_archive(self, archive_path: str | Path) -> dict:
|
||||
import zipfile
|
||||
|
||||
Reference in New Issue
Block a user