T09 · Insecure Skill Coding Practices
Warning
- Location
- parse_ocr.py:26
- Finding
- Unrestricted Transmission of Complete Documents to Plaintext-Capable, Caller-Controlled Endpoints<![CDATA[ ## Vulnerability Details **File Locations**: - `parse_ocr.py:26-40, 68` - `auto_run.py:15-16, 43-47, 76-77, 103` - `write_excel.py:8, 25, 33` **Vulnerability Type**: Sensitive data transmission to an unrestricted endpoint with plaintext HTTP support **Risk Level**: Medium ### Vulnerable Code #### `parse_ocr.py:26-40` ```python boundary = uuid.uuid4().hex with open(path, "rb") as f: data = f.read() body = (f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file"; filename="{fname}"\r\n' f"Content-Type: application/octet-stream\r\n\r\n").encode() + data + f"\r\n--{boundary}--\r\n".encode() req_url = f"{url}/parse" if sync else f"{url}/parse-async" if owner and not sync: req_url += "?" + urllib.parse.urlencode({"owner": owner}) req = urllib.request.Request( req_url, data=body, method="POST", headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, json.loads(resp.read().decode("utf-8")) ``` #### `parse_ocr.py:68` ```python ap.add_argument("--url", default=os.environ.get("EXAM_OCR_URL", "http://exam-ocr:8000")) ``` #### `auto_run.py:15-16, 43-47` ```python DEFAULT_URL = os.environ.get("EXAM_OCR_URL", "http://exam-ocr:8000") API_URL = "https://api.deepseek.com/chat/completions" ``` ```python def ocr(url, pdf): with open(pdf, "rb") as f: r = requests.post(f"{url}/parse", files={"file": (os.path.basename(pdf), f, "application/octet-stream")}, timeout=600) ``` #### `auto_run.py:76-77` ```python def write_excel(url, records, out): r = requests.post(f"{url}/write-excel", json={"schema": SCHEMA, "records": records}, timeout=120) ``` #### `auto_run.py:103` ```python ap.add_argument("--url", default=DEFAULT_URL) ``` #### `write_excel.py:25, 33` ```python ap.add_argument("--url", default=os.environ.get("EXAM_OCR_URL", "http://exam-ocr ...[truncated 3251 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Require encrypted transport** - Accept only `https://` OCR URLs by default. - Permit `http://` only for explicitly approved loopback or isolated container-network hosts. - Fail closed when an unsupported scheme is supplied. 2. **Restrict destination hosts** - Introduce an explicit OCR host allowlist. - Compare the parsed hostname and port against approved configuration. - Reject URLs containing embedded credentials, fragments, unexpected paths, or non-HTTP schemes. - Resolve and validate destinations carefully if private-network restrictions are required. 3. **Add an explicit plaintext opt-in** - If compatibility requires HTTP, require a flag such as `--allow-insecure-http`. - Display a clear warning that the complete document or record set will be transmitted without transport encryption. 4. **Authenticate the OCR service** - Support an API token or mutual TLS. - Avoid placing credentials in URL query strings. - Store credentials in protected environment variables or a secret manager. 5. **Reduce transmitted data** - Generate Excel files locally where practical, avoiding retransmission of extracted records. - Send only the minimum document pages or fields needed for the requested operation. - Avoid unnecessary retention of uploaded files and OCR results on the service. 6. **Validate remote responses** - Enforce expected response content types and maximum response sizes. - Validate OCR JSON structure before processing it. - Validate downloaded Excel content before writing it as the final output. 7. **Improve user disclosure** - State clearly before execution that complete source documents are sent to the OCR service. - State that one-click mode also sends OCR text to DeepSeek. - Document the service's retention, access-control, and deletion requirements. ]]>
