T09 · Insecure Skill Coding Practices
- Location
scripts/patentmax_oa.py:471- Finding
Remote document metadata can escape the selected output directory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/patentmax_oa.py, lines 471–477
Vulnerability Type: Path traversal leading to arbitrary file creation
Risk Level: Mediumpython for doc in result.get("documents") or [{"key": "statement", "title": "意见陈述书", "formal": True, "file": "statement.docx"}]: content = try_download(f"/api/v1/reports/{urllib.parse.quote(report_id)}/files/{urllib.parse.quote(doc.get('file') or '')}") if not content: continue path = unique_path(os.path.join(target, f"{doc.get('title') or doc.get('key')}_{stem}.docx")) with open(path, "wb") as fh: fh.write(content)Technical Analysis
The filename used for downloaded reports incorporates
result.documents[].title, or the fallbackresult.documents[].key, without sanitizing path separators or checking that the resolved path remains inside the user-selected output directory.These fields originate from the remote PatentMax API response. A value containing traversal components, such as
../../outside/report, causesos.path.join()andos.path.abspath()inunique_path()to resolve the destination outsidetarget. A value beginning with an absolute-path prefix can similarly cause the output directory to be discarded.unique_path()only avoids replacing an already existing exact path. It does not enforce directory containment, so it does not prevent traversal. The downloaded remote bytes are subsequently written to the escaped destination.Attack Path
- A user submits a report and later invokes the delivery path after the remote operation succeeds.
- The configured API returns a report result containing a crafted
documents[].titleordocuments[].key, such as../../attacker-controlled/report. deliver()combines that remote value with the local output directory without sanitization.unique_path()converts the combined value to an absolute path but does not verify that ...[truncated 970 chars]
- Remediation
View remediation
Remediation Suggestions
- Convert remote document titles and keys into safe filename components using a strict allowlist. Remove path separators, drive prefixes, traversal sequences, control characters, and reserved filesystem characters.
- Apply the existing
safe_name()logic, or a dedicated equivalent, to every server-provided filename component. - Resolve both the output directory and candidate destination, then enforce containment before writing:
python safe_title = safe_name(doc.get("title") or doc.get("key") or "document") root = os.path.realpath(target) candidate = os.path.realpath( os.path.join(root, f"{safe_title}_{stem}.docx") ) if os.path.commonpath([root, candidate]) != root: raise ValueError("Document path escapes the output directory") path = unique_path(candidate)- Repeat the containment check after
unique_path()so future changes to collision handling cannot reintroduce an escape. - Consider mapping known document keys to fixed local filenames instead of trusting remote display titles.
- Add regression tests for
../, absolute POSIX paths, Windows drive paths, UNC paths, nested separators, and mixed separator encodings.
