T09 · Insecure Skill Coding Practices
- Location
scripts/radar.py:44- Finding
Scan Limits and Exclusion Controls Do Not Restrict File Content Access
- Content
View full analysis
Vulnerability Details
File Location:
scripts/radar.py:44-60,scripts/radar.py:71-75,scripts/radar.py:111-116,scripts/radar.py:166,scripts/radar.py:177-179
Vulnerability Type: Inadequate path-boundary enforcement and resource limiting
Risk Level: MediumVulnerable Code
python def scan_project(root: Path, max_files: int = 5000) -> dict: """Scan project structure.""" info = {"files": 0, "dirs": 0, "loc": 0, "languages": Counter(), "extensions": Counter()} if not root.exists(): return info for f in root.rglob("*"): if f.is_dir(): # Skip hidden dirs and common dependency dirs if f.name.startswith(".") or f.name in ("node_modules", "__pycache__", "target", "build", "dist", ".git"): continue info["dirs"] += 1 elif f.is_file(): info["files"] += 1 ext = f.suffix.lstrip(".") or "noext" info["extensions"][ext] += 1 if info["files"] > max_files: break return infoThe dependency scan independently traverses and reads files without applying the configured file limit:
python for f in sorted(root.rglob("*")): if not f.is_file() or f.name.startswith("."): continue ext = f.suffix.lower() lang = NoneThe technical-debt scan similarly reads every matching entry without a shared limit or path-boundary check:
python for f in sorted(root.rglob("*")): if not f.is_file() or f.name.startswith("."): continue try: text = f.read_text(encoding="utf-8", errors="replace") except Exception: continueThe exclusion option is accepted but never used:
python parser.add_argument("--exclude", nargs="+", default=[])All scan phases run regardless of the project scan's
max_fileslimit:python project = scan_projec ...[truncated 2966 chars]- Remediation
View remediation
Remediation Suggestions
- Replace independent
rglob()traversals with one centralized traversal usingos.walk()so excluded directories can be removed fromdirnamesbefore descent. - Normalize the root once with
root = root.resolve(strict=True). - Resolve each file candidate and verify that it remains beneath the normalized root before opening it:
python resolved = candidate.resolve(strict=True) try: resolved.relative_to(root) except ValueError: continue - Reject symbolic links by default with
candidate.is_symlink(), or expose an explicit opt-in option for following them. - Compile
--excludevalues into normalized path or glob rules and apply them consistently to project counting, dependency scanning, and technical-debt scanning. - Enforce one shared file budget across every scan phase rather than limiting only metadata collection.
- Add a maximum cumulative byte budget and a per-file size limit before calling
read_text(). - Avoid materializing complete traversals with
sorted(root.rglob("*"))for untrusted or large repositories. - Clearly report skipped files, exhausted limits, symlink rejections, and exclusions so users can assess scan completeness.
- Add tests covering hidden directories, nested dependency directories, exclusions, oversized trees, and symlinks resolving outside the root.
- Replace independent
