T09 · Insecure Skill Coding Practices
- Location
scripts/run.py:27- Finding
Unbounded File Reads Can Cause Resource Exhaustion
- Content
View full analysis
Vulnerability Details
File Location:
scripts/run.py, lines 27–31 and 136–146
Vulnerability Type: Uncontrolled resource consumption
Risk Level: MediumComplete Code Snippet
python def read_text(path: Path) -> str: try: return path.read_text(encoding="utf-8") except UnicodeDecodeError: return path.read_text(encoding="utf-8", errors="replace")The vulnerable function is called during pattern scanning:
python def pattern_report(spec: dict, path: Path, limit: int) -> str: targets = [path] if path.is_file() else list_text_files(path, limit=limit) findings = [] for target in targets: text = read_text(target) for name, pattern in PATTERNS.items(): for match in re.finditer(pattern, text, flags=re.IGNORECASE): snippet = match.group(0) if "secret_like" == name: snippet = re.sub(r"([A-Za-z0-9_\-]{4})[A-Za-z0-9_\-]+", r"\1***", snippet) findings.append((str(target), name, snippet[:160]))Technical Analysis
The scanner loads every selected file completely into memory with
Path.read_text(). The--limitoption limits the number of files or findings, but it does not impose a maximum size per file or a maximum total number of bytes read.After allocating memory for the complete file, the implementation applies several regular expressions to the full string. This adds CPU consumption and may create further temporary objects. The fallback after a decoding error reads the complete file again, potentially doubling I/O and allocation pressure for malformed or non-UTF-8 content.
An attacker who can influence the input file or directory can exploit this behavior without requiring command execution, elevated privileges, or a specially crafted binary payload. An oversized file with any accepted extension, such as
.md,.txt,.json, or.py, is sufficient.Attack Path
- An attacker creates or sup ...[truncated 1103 chars]
- Remediation
View remediation
Remediation Suggestions
- Enforce a conservative maximum file size before reading by checking
path.stat().st_size. - Enforce a cumulative byte budget across all files in a directory scan.
- Process input incrementally in bounded chunks rather than loading complete files into memory.
- Preserve a small overlap between chunks so patterns spanning chunk boundaries can still be detected.
- Reject oversized files with an explicit report entry instead of silently skipping them.
- Validate
--limitand reject negative or unreasonably large values. - Apply process-level memory and execution-time limits when running the scanner in an Agent environment.
- Avoid reading a malformed file twice; use a single bounded read with controlled decoding behavior.
- Add automated tests using oversized and malformed files to verify that scanning terminates safely and reports truncation.
A minimal size guard should follow this pattern:
python MAX_FILE_BYTES = 10 * 1024 * 1024 def read_text(path: Path) -> str: size = path.stat().st_size if size > MAX_FILE_BYTES: raise ValueError( f"File exceeds the {MAX_FILE_BYTES}-byte scanning limit: {path}" ) return path.read_text(encoding="utf-8", errors="replace")For stronger protection, replace the complete read with bounded streaming and track the total bytes processed across the scan.
- Enforce a conservative maximum file size before reading by checking
