hermes - 💡(How to fix) Fix Symlinked skills trigger false "outside trusted skills directory" security warning [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Skills that are symlinks under ~/.hermes/skills/ trigger a false security warning: "skill file is outside the trusted skills directory (~/.hermes/skills/)". The warning fires even though the symlink itself lives inside the trusted directory — the check resolves the symlink to its target, which may be elsewhere on the filesystem.

This is a separate issue from the _find_skill / rglob discovery bug (#35184), though both are caused by symlinks. PRs #35213 and #35244 fix the discovery bug in skill_manager_tool.py but do not address this warning in skills_tool.py.

Error Message

_trusted_dirs = [SKILLS_DIR.resolve()] for _td in _trusted_dirs: try: skill_md.resolve().relative_to(_td) # ← resolves symlink to target _outside_skills_dir = False break except ValueError: continue

Root Cause

This is a separate issue from the _find_skill / rglob discovery bug (#35184), though both are caused by symlinks. PRs #35213 and #35244 fix the discovery bug in skill_manager_tool.py but do not address this warning in skills_tool.py.

Fix Action

Fixed

Code Example

mkdir -p /opt/my-skills/example-skill
   echo -e "---\nname: example-skill\ndescription: Test\n---\n# Test" > /opt/my-skills/example-skill/SKILL.md

---

ln -s /opt/my-skills/example-skill ~/.hermes/skills/example-skill

---

WARNING tools.skills_tool: Skill security warning for 'example-skill': skill file is outside the trusted skills directory (~/.hermes/skills/): /opt/my-skills/example-skill/SKILL.md

---

_trusted_dirs = [SKILLS_DIR.resolve()]
for _td in _trusted_dirs:
    try:
        skill_md.resolve().relative_to(_td)  # ← resolves symlink to target
        _outside_skills_dir = False
        break
    except ValueError:
        continue

---

_outside_skills_dir = True
_trusted_dirs = [SKILLS_DIR.resolve()]
try:
    _trusted_dirs.extend(d.resolve() for d in all_dirs[1:])
except Exception:
    pass
for _td in _trusted_dirs:
    try:
        # Check unresolved path first — a symlink inside the trusted dir is trusted
        # even if its target is elsewhere (e.g., external skill repos symlinked in)
        skill_md.relative_to(_td)
        _outside_skills_dir = False
        break
    except ValueError:
        pass
    try:
        skill_md.resolve().relative_to(_td)
        _outside_skills_dir = False
        break
    except ValueError:
        continue
RAW_BUFFERClick to expand / collapse

Bug: Symlinked skills trigger false "outside trusted skills directory" security warning

Summary

Skills that are symlinks under ~/.hermes/skills/ trigger a false security warning: "skill file is outside the trusted skills directory (~/.hermes/skills/)". The warning fires even though the symlink itself lives inside the trusted directory — the check resolves the symlink to its target, which may be elsewhere on the filesystem.

This is a separate issue from the _find_skill / rglob discovery bug (#35184), though both are caused by symlinks. PRs #35213 and #35244 fix the discovery bug in skill_manager_tool.py but do not address this warning in skills_tool.py.

Reproduction

  1. Create a skill outside ~/.hermes/skills/:

    mkdir -p /opt/my-skills/example-skill
    echo -e "---\nname: example-skill\ndescription: Test\n---\n# Test" > /opt/my-skills/example-skill/SKILL.md
  2. Symlink it into the trusted directory:

    ln -s /opt/my-skills/example-skill ~/.hermes/skills/example-skill
  3. Load the skill in Hermes (via skill_view, cron job, or agent session).

  4. Observe the log:

    WARNING tools.skills_tool: Skill security warning for 'example-skill': skill file is outside the trusted skills directory (~/.hermes/skills/): /opt/my-skills/example-skill/SKILL.md

Expected Behavior

A symlink whose unresolved path is inside ~/.hermes/skills/ should be considered trusted. The security check should only warn when the symlink itself lives outside the trusted directory (e.g., loaded from skills.external_dirs or a path injection).

Actual Behavior

tools/skills_tool.py line 1029 uses skill_md.resolve() which follows the symlink to its target:

_trusted_dirs = [SKILLS_DIR.resolve()]
for _td in _trusted_dirs:
    try:
        skill_md.resolve().relative_to(_td)  # ← resolves symlink to target
        _outside_skills_dir = False
        break
    except ValueError:
        continue

When the symlink target is outside ~/.hermes/skills/ (which is the whole point of symlinking — the canonical source lives elsewhere), resolve().relative_to() raises ValueError and the warning fires.

Proposed Fix

Check the unresolved path first. If the symlink itself is inside the trusted directory, mark it as trusted regardless of where the target lives:

_outside_skills_dir = True
_trusted_dirs = [SKILLS_DIR.resolve()]
try:
    _trusted_dirs.extend(d.resolve() for d in all_dirs[1:])
except Exception:
    pass
for _td in _trusted_dirs:
    try:
        # Check unresolved path first — a symlink inside the trusted dir is trusted
        # even if its target is elsewhere (e.g., external skill repos symlinked in)
        skill_md.relative_to(_td)
        _outside_skills_dir = False
        break
    except ValueError:
        pass
    try:
        skill_md.resolve().relative_to(_td)
        _outside_skills_dir = False
        break
    except ValueError:
        continue

This preserves the existing behavior for non-symlink paths (resolve check still works) while correctly handling symlinks that live inside the trusted directory.

Environment

  • hermes-agent: present in current main (verified in tools/skills_tool.py line 1029)
  • Python: 3.13 (but affects all versions — Path.resolve() follows symlinks since Python 3.6)

Impact

  • Cosmetic: The warning does not prevent the skill from loading (the check only logs, it doesn't block). However, it fills logs with false positives and makes it harder to spot real security issues.
  • Downstream confusion: Agents and operators may investigate the warning thinking it's a real security problem.
  • Log noise: On systems with many symlinked skills (e.g., skills linked from a shared repo), every skill load generates a spurious warning.

Related

  • #35184 — skill_manage discovery bug (same root cause: symlinks, different file)
  • #35213 — PR fixing discovery in skill_manager_tool.py (does not touch skills_tool.py)
  • #35244 — Competing PR fixing discovery in skill_manager_tool.py (does not touch skills_tool.py)
  • #8293 — Original symlinked skills discovery report

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix Symlinked skills trigger false "outside trusted skills directory" security warning [2 pull requests]