T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/main.py:70
- Finding
- Image size validation occurs after an unbounded file read<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:70-84` **Vulnerability Type**: Unbounded memory allocation and denial of service **Risk Level**: Medium ### Vulnerable Code ```python if os.path.isfile(value): with open(value, "rb") as f: raw = f.read() # If the file itself contains Base64 text, return it directly. try: raw_str = raw.decode("utf-8").strip() base64.b64decode(raw_str, validate=True) return raw_str except (UnicodeDecodeError, ValueError): pass # Otherwise, encode the binary file as Base64. if len(raw) > MAX_IMAGE_SIZE_BYTES: print(f"Error: Image exceeds the {MAX_IMAGE_SIZE_BYTES // (1024 * 1024)} MB limit", file=sys.stderr) sys.exit(1) encoded = base64.b64encode(raw).decode("utf-8") ``` The comments and error message above are translated into English for presentation; the executable behavior matches the audited source. ### Technical Analysis The code calls `f.read()` without a size argument, loading the complete user-selected file into memory before checking whether it exceeds `MAX_IMAGE_SIZE_BYTES`. The nominal 10 MB restriction therefore does not protect the process from memory exhaustion. The Base64-text branch also returns immediately after validating the encoding syntax. It does not enforce the decoded-size limit before returning, so an oversized Base64 document can bypass the intended input-size validation. This is an insecure resource-handling issue rather than evidence of covert exfiltration. The Base64 conversion itself is necessary for the declared Tencent Cloud OCR API and is not malicious. ### Attack Path 1. An attacker creates or identifies a file substantially larger than available process memory. 2. The attacker causes the Skill to be invoked with that path through `--image-base64`. 3. `os.path.isfile()` accepts the path. 4. `f.read()` attempts to load the entire file before the size check is reached. 5. The Python proc ...[truncated 899 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Check regular-file size with `os.stat()` before opening or reading it. 2. Reject files larger than the accepted encoded-input threshold. 3. Use a bounded read such as `f.read(MAX_IMAGE_SIZE_BYTES + 1)` and reject input when the additional byte is present. This protects against file-size changes between the metadata check and read operation. 4. For Base64 text, estimate and then verify decoded size before returning it. 5. Decode Base64 incrementally or with strict input limits to avoid simultaneously retaining large encoded and decoded copies. 6. Consider rejecting non-regular files to avoid blocking or unbounded reads from devices, pipes, and special files. 7. Catch `OSError`, `MemoryError`, and decoding errors and return a controlled failure. Example hardening pattern: ```python file_size = os.path.getsize(value) if file_size > MAX_ENCODED_INPUT_SIZE: raise ValueError("Input file is too large") with open(value, "rb") as file: raw = file.read(MAX_ENCODED_INPUT_SIZE + 1) if len(raw) > MAX_ENCODED_INPUT_SIZE: raise ValueError("Input file is too large") try: raw_str = raw.decode("ascii").strip() decoded = base64.b64decode(raw_str, validate=True) if len(decoded) > MAX_IMAGE_SIZE_BYTES: raise ValueError("Decoded image is too large") return raw_str except (UnicodeDecodeError, ValueError): pass ``` ]]>
