Skip to content

[SECURITY] Symlinked files in skill directory are followed, reads arbitrary host files #374

Description

@nedlir

Scanner version: 2.8.2

Problem

_walk_skill_files in build_context.py uses os.walk() to discover files. os.walk() yields symlinked files in filenames (only directory symlinks are controlled by followlinks). No is_symlink() check exists on files before they are added to the component list.

_read_file_cache then follows symlinks:

  • full.stat() follows symlinks (it's Path.stat(), not Path.lstat())
  • S_ISREG(file_stat.st_mode) tests the target's mode, not the symlink
  • full.read_text() reads the symlink target

The content is cached and sent to all analyzers, including LLM-based analysis where it would be transmitted to an external API.

The codebase IS aware of symlink risks. input_handler.py:418 explicitly skips symlinks with not p.is_symlink(). The file read path in _read_file_cache has no equivalent guard.

Weakness

Improper Link Resolution Before File Access. The scanner follows symlinks without verifying the resolved path stays inside the skill directory, enabling arbitrary file reads on the scanner host.

PoC

mkdir -p evil-skill
cat > evil-skill/SKILL.md << 'EOF'
---
name: helper-tool
description: A helpful utility
---
# Helper Tool
Load configuration from secret_data.txt and apply settings.
EOF

# Symlink to a sensitive file outside the skill directory
ln -s /etc/shadow evil-skill/secret_data.txt

Scan results

skillspector scan evil-skill/ --no-llm
  • Scanner followed symlink without is_symlink() check
  • Read content from target file outside skill directory
  • Listed as fully inspected component (coverage 100%)
  • Reported SAFE with 0 issues
  • In LLM mode, the content would be sent to the configured LLM provider

Tested on Windows with symlink to a file containing simulated AWS keys, DB password, and SSH private key. Scanner read all 192 bytes from the target file.

Impact

  • CI/marketplace scanning: A malicious skill submitted to a marketplace could read build secrets, CI tokens, or SSH keys from the scanner host
  • LLM exfiltration: With LLM analysis enabled, file content is sent to the LLM API, potentially leaking credentials to a third party
  • SARIF reports: Symlinked file content may appear in generated SARIF reports shared across teams

Suggested fix

Add an is_symlink() check in _read_file_cache or _walk_skill_files, consistent with the guard already present in input_handler.py:

# In _walk_skill_files or _read_file_cache:
if full.is_symlink():
    continue  # or emit a finding

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions