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
Scanner version: 2.8.2
Problem
_walk_skill_filesinbuild_context.pyusesos.walk()to discover files.os.walk()yields symlinked files infilenames(only directory symlinks are controlled byfollowlinks). Nois_symlink()check exists on files before they are added to the component list._read_file_cachethen follows symlinks:full.stat()follows symlinks (it'sPath.stat(), notPath.lstat())S_ISREG(file_stat.st_mode)tests the target's mode, not the symlinkfull.read_text()reads the symlink targetThe 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:418explicitly skips symlinks withnot p.is_symlink(). The file read path in_read_file_cachehas 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
Scan results
is_symlink()checkTested 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
Suggested fix
Add an
is_symlink()check in_read_file_cacheor_walk_skill_files, consistent with the guard already present ininput_handler.py: