T09 · Insecure Skill Coding Practices
- Location
scripts/release_preflight.py:101- Finding
Unbounded Out-of-Root File Reads Through Symbolic Links
- Content
View full analysis
Vulnerability Details
File Location:
scripts/release_preflight.py, lines 101-105 and 113-120
Vulnerability Type: Symbolic-link following, insufficient path-boundary validation, and unbounded file reads
Risk Level: MediumVulnerable Code
python def scan_text_file(path: Path): try: return path.read_text(errors='ignore') except Exception: return ''The vulnerable function is reached from the identity-leak scanner:
python def find_identity_leaks(root: Path): hits = [] for path in list_paths(root): if should_skip_identity_scan(root, path): continue if not path.is_file() or path.suffix.lower() not in TEXT_EXTS: continue text = scan_text_file(path)Technical Analysis
The scanner recursively discovers paths beneath an audit target and reads files whose names have supported text extensions. It does not reject symbolic links or resolve each candidate and verify that the resolved path remains beneath the target root.
Both
Path.is_file()andPath.read_text()follow symbolic links. Consequently, a symbolic link located inside an attacker-controlled bundle can point to a readable file outside the intended audit directory. If the link has an accepted extension, such asprobe.md, the scanner opens and reads its external target with the privileges of the scanner process.The complete file is also loaded into memory without a maximum size. A link to an oversized file or suitable special file can therefore consume excessive memory, stall the audit, or otherwise deny service. The broad exception handler does not prevent resource exhaustion that occurs while reading.
The current report does not reproduce arbitrary file contents. However, it can expose whether predefined path markers occur in the external file, creating a limited content oracle and confirming information about files outside the auth ...[truncated 1593 chars]
- Remediation
View remediation
Remediation Suggestions
-
Reject symbolic links before checking or reading candidate files:
python if path.is_symlink(): return '' -
Resolve both the root and candidate path, then require the candidate to remain beneath the resolved root:
python resolved_root = root.resolve() resolved_path = path.resolve(strict=True) try: resolved_path.relative_to(resolved_root) except ValueError: # Skip and report an out-of-root path. continue -
Verify that candidates are regular files rather than devices, FIFOs, sockets, or other special files. Use
lstat()where appropriate so validation does not silently follow links. -
Apply a conservative maximum file size using metadata before opening the file. Treat files exceeding the threshold as skipped findings rather than reading them completely.
-
Read content incrementally and stop after a bounded number of bytes instead of using unrestricted
read_text(). -
Report skipped symbolic links, out-of-root paths, special files, and oversized files so users understand that the scan was incomplete.
-
Where race conditions are relevant, open files using platform facilities that prohibit symbolic-link following, then validate the opened descriptor rather than relying only on path checks performed before opening.
-
