T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:56- Finding
Unrestricted OCR Endpoint Allows Disclosure of API Credentials and Sensitive Documents
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py:56-60, 78, 91-93, 117-129
Vulnerability Type: Arbitrary outbound destination for sensitive data
Risk Level: HighVulnerable Code
python if '=' in line: key, value = line.split('=', 1) key = key.strip() value = value.strip().strip('"').strip("'") config[key] = value config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1') return configpython api_base = config['SCNET_API_BASE'] api_key = config['SCNET_API_KEY'] url = f"{api_base}/ocr/recognize"python headers = { 'Authorization': f'Bearer {api_key}' } try: with open(file_path, 'rb') as f: files = { 'file': (os.path.basename(file_path), f, mime_type) } data = { 'ocrType': ocr_type, 'channelTag': "scnetSkills" } response = requests.post( url, headers=headers, data=data, files=files, timeout=60 )Technical Analysis
The
SCNET_API_BASEconfiguration value is used directly to construct the request URL. The implementation does not validate:- That the URL uses HTTPS.
- That its hostname is exactly
api.scnet.cn. - That it uses an approved port.
- That the URL has no embedded credentials or unexpected path components.
- That redirects remain within the approved Scnet origin.
The resulting request contains both the complete user-selected document and the
SCNET_API_KEYbearer credential. Therefore, changingSCNET_API_BASEcan redirect both sensitive assets to an arbitrary server, including over plaintext HTTP.This behavior exceeds the declared outbound permission in
skill.yaml:27andSKILL.md:22, which describes network access specifically to the Scnet OCR API atapi.scnet.cn.Uploading the selected document to the default Scnet endpoint is disclosed and required for the declared remote OCR functionality. The vulnerabili ...[truncated 1704 chars]
- Remediation
View remediation
Remediation Suggestions
-
Remove arbitrary endpoint configurability where it is unnecessary. Use a constant approved endpoint:
python OCR_URL = "https://api.scnet.cn/api/llm/v1/ocr/recognize" -
If endpoint configuration is operationally required, enforce a strict allowlist. Parse the URL with
urllib.parse.urlsplitand require:- Scheme exactly
https. - Hostname exactly
api.scnet.cn. - Port absent or explicitly approved.
- No username or password component.
- An approved path prefix.
- No fragments or other ambiguous components.
- Scheme exactly
-
Construct the endpoint from validated components rather than concatenating an unrestricted string.
-
Disable redirects for requests containing credentials and documents, or validate every redirect target before following it:
python response = requests.post( url, headers=headers, data=data, files=files, timeout=60, allow_redirects=False, ) -
Fail closed when endpoint validation fails. Do not fall back to an unverified destination.
-
Keep declared permissions consistent with implementation. If only
api.scnet.cnis declared, runtime controls must technically enforce that boundary. -
Add automated security tests covering malicious configurations such as:
http://api.scnet.cnhttps://attacker.examplehttps://api.scnet.cn.attacker.example- URLs with embedded credentials
- URLs using unexpected ports
- Redirect responses to unapproved hosts
-
Add explicit upload authorization as defense in depth. The documentation requires prior affirmative consent, but the script only prints a warning. Require a dedicated confirmation flag or interactive approval before transmitting the document.
-
Rotate the API key and investigate document exposure if an untrusted
SCNET_API_BASEvalue has previously been used.
-
