T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/git_stats.py:102
- Finding
- Tracked Symbolic Links Can Cause Out-of-Repository File Reads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/git_stats.py`, lines 102–110 **Vulnerability Type**: Improper path containment and symbolic-link handling **Risk Level**: Medium ```python for f in lines: if not f: continue filepath = os.path.join(cwd, f) if not os.path.isfile(filepath): continue ext = os.path.splitext(f)[1].lower() or "(no ext)" try: with open(filepath, "r", errors="ignore") as fh: count = sum(1 for _ in fh) ``` ### Technical Analysis The LOC calculation obtains tracked paths from `git ls-files`, joins each path to the repository root, and opens the resulting filesystem path. Both `os.path.isfile()` and `open()` follow symbolic links. A Git repository can contain a tracked symbolic link whose target is outside the repository. The implementation does not reject symbolic links or verify that the resolved path remains beneath the resolved repository root. Consequently, analyzing an attacker-controlled repository can cause the process to read an arbitrary external file, provided the file is readable under the privileges of the user running the Skill. The file content is not directly printed. However, the code reads the complete target and incorporates its line count into the aggregated LOC results, creating an unauthorized file-access and limited information-disclosure condition. ### Attack Path 1. An attacker creates a Git repository containing a tracked symbolic link that points to a sensitive local path, such as a predictable configuration or credential file. 2. The attacker convinces a user or automated agent to analyze that repository with this Skill. 3. LOC counting runs by default because `--no-loc` is not enabled. 4. `git ls-files` returns the tracked symbolic-link path. 5. `os.path.isfile()` follows the link and accepts it when its target is a regular file. 6. `open()` follows the same link and reads the external target in full. 7. The target's line count is inclu ...[truncated 730 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject symbolic links before opening tracked paths: ```python if os.path.islink(filepath): continue ``` 2. Resolve both the repository root and candidate path, then enforce containment: ```python root_real = os.path.realpath(cwd) filepath_real = os.path.realpath(os.path.join(root_real, f)) try: if os.path.commonpath([root_real, filepath_real]) != root_real: continue except ValueError: continue ``` 3. Open only the validated resolved path and, where supported, use no-follow semantics to reduce time-of-check/time-of-use risks. 4. Prefer reading repository blob data through Git, such as `git show HEAD:<path>` or an equivalent plumbing command. This counts the versioned object rather than following working-tree links and better matches the stated purpose of analyzing tracked repository content. 5. Add regression tests covering: - A tracked symlink to a file outside the repository. - A tracked symlink to a directory outside the repository. - Nested paths that resolve outside the repository. - Ordinary tracked files that remain within the repository. ]]>
