fix(skills): scope skill reads to caller owner (#777)

read_skill_md and read_skill_reference walk all skill files via
_iter_skill_files and return the first match by slug, regardless
of owner. In a multi-user deployment where two users have skills
with the same slug under different categories, a caller scoped
to owner='alice' can read Bob's skill content.

This is the same cross-tenant leak class as the update_skill /
delete_skill fix (PR #755, merged), but on the read path.

Changes:
- read_skill_md / read_skill_reference accept owner= param (default
  None = match ownerless only, matching the write-path convention).
- 7 callers updated: tool_implementations.py (view, view_ref, patch),
  builtin_actions.py (test_skills), skills_routes.py (audit, source,
  test routes).
- Tests: read scoping (alice reads hers, not bob's), positive update
  scoping (alice can mutate her own), ownerless-match default.
This commit is contained in:
Ernest Hysa
2026-06-02 03:21:27 +01:00
committed by GitHub
parent 000bd6d1ab
commit f4aef0dcf7
5 changed files with 89 additions and 15 deletions
+1 -1
View File
@@ -1318,7 +1318,7 @@ async def action_test_skills(owner: str, **kwargs) -> Tuple[str, bool]:
name = skill.get("name")
if not name:
continue
md = sm.read_skill_md(name) or ""
md = sm.read_skill_md(name, owner=owner) or ""
if not md:
tally["skipped"] += 1
per_skill_log.append(f"{name}: skipped (no SKILL.md)")