- Location
- scripts/generate.py:86
- Finding
- Unsanitized API response permits arbitrary filesystem paths and unrestricted remote downloads## Vulnerability Details
**File Location**: `scripts/generate.py`, lines 86-95 and 192-212
**Vulnerability Type**: Untrusted path construction, unrestricted URL handling, and unbounded download
**Risk Level**: Medium
### Vulnerable Code
```python
def download_file(url, output_path):
"""下载文件"""
try:
req = request.Request(url, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.0'
})
with request.urlopen(req, timeout=60) as response:
with open(output_path, 'wb') as f:
f.write(response.read())
return True
except Exception as e:
print(f"下载文件失败: {e}")
return False
```
```python
job_id = result.get("JobId")
if not job_id:
print("❌ 未获取到JobId")
print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}")
sys.exit(1)
print(f"✅ 任务提交成功,Job ID: {job_id}")
# 等待完成
final_result = wait_for_completion(api_key, job_id)
if not final_result:
sys.exit(1)
# 创建输出目录
today = datetime.now().strftime("%Y%m%d")
output_dir = Path(args.output) / today / job_id
output_dir.mkdir(parents=True, exist_ok=True)
# 保存任务信息
info_path = output_dir / "info.json"
with open(info_path, "w", encoding="utf-8") as f:
json.dump(final_result, f, ensure_ascii=False, indent=2)
# 下载3D模型
model_url = final_result.get("ResultUrl", "")
if model_url:
# 根据URL后缀判断格式
ext = model_url.split('.')[-1].split('?')[0] if '.' in model_url else "glb"
model_path = output_dir / f"model.{ext}"
if download_file(model_url, model_path):
print(f"✅ 已保存: {model_path}")
```
### Technical Analysis
The script treats `JobId` and `ResultUrl` from the remote API as trusted values.
`JobId` is directly appended to the user-selected output path. There is no validation requiring it to be a simple identifier and no canonical-path containment check. A response contai
...[truncated 3010 chars]
- Remediation
- ## Remediation Suggestions
1. Validate `JobId` using a strict allowlist suitable for Tencent job identifiers, for example letters, digits, hyphens, and underscores with a conservative maximum length.
2. Reject absolute paths, path separators, `.` components, and `..` components in all remote values used as filenames or directory names.
3. Resolve the base output directory and candidate destination with `Path.resolve()`, then verify that the destination remains beneath the resolved base directory before creating or writing anything.
4. Require model URLs to use HTTPS and allowlist the exact Tencent download hosts documented for the service.
5. Validate every redirect destination rather than only the initial URL. Disable automatic redirects or use a custom redirect handler that reapplies scheme, hostname, port, and IP-range checks.
6. Resolve destination hosts and reject loopback, link-local, multicast, unspecified, and private-network addresses unless such access is explicitly required.
7. Stream downloads in bounded chunks instead of calling `response.read()` without a limit. Abort when a configured maximum model size is exceeded.
8. Check `Content-Length` when present, while still enforcing the limit during streaming because that header may be absent or inaccurate.
9. Restrict output formats to an explicit allowlist such as `glb` and `obj`. Prefer trusted response metadata or a fixed local extension rather than deriving an extension from arbitrary URL text.
10. Write downloads to a safely created temporary file within the validated output directory, verify size and expected format, and atomically rename the file only after validation succeeds.
11. Use exclusive file creation or an explicit overwrite policy to prevent unintended replacement of existing files.