Back to skill

Security audit

审查意见答复 · PatentMax

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its stated patent-drafting purpose, but it uploads sensitive patent materials to a third-party service and has a file-writing weakness that warrants Review before installation.

Install only if you are comfortable sending full Chinese patent office-action materials and application text to PatentMax/ip930.com for paid processing. Review confidentiality, client authorization, and export-control constraints first. Treat generated outputs as drafts for patent-agent review, and be aware that the current client should sanitize server-provided output filenames before writing downloaded Word files locally.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
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: Medium

python
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 fallback result.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, causes os.path.join() and os.path.abspath() in unique_path() to resolve the destination outside target. 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

  1. A user submits a report and later invokes the delivery path after the remote operation succeeds.
  2. The configured API returns a report result containing a crafted documents[].title or documents[].key, such as ../../attacker-controlled/report.
  3. deliver() combines that remote value with the local output directory without sanitization.
  4. 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.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Tainted flow: 'req' from os.environ.get (line 192, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/patentmax_oa.py (reported line 126)May include surrounding context.

python
req.add_header("Content-Type", "application/json")
    req.add_header("Accept", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=30) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as exc:
        _, message = _server_error(exc.read().decode("utf-8", "replace"))

Tainted flow: 'req' from os.environ.get (line 192, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/patentmax_oa.py (reported line 199)May include surrounding context.

python
req.add_header("Content-Type", "application/json")
    for key, value in (headers or {}).items():
        req.add_header(key, value)
    return urllib.request.urlopen(req, timeout=TIMEOUT)


def request(path, method="GET", body=None, headers=None, raw=False):

Lp1

High
Category
MCP Least Privilege
Confidence
98% confidence
Finding

Undeclared network capability is a real security issue for this skill because its core behavior is uploading office actions, applications, and possibly cited references to a third-party endpoint. In the patent domain, these documents can contain confidential invention details, so hidden transmission materially increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
98% confidence
Finding

Undeclared network capability is a real security issue for this skill because its core behavior is uploading office actions, applications, and possibly cited references to a third-party endpoint. In the patent domain, these documents can contain confidential invention details, so hidden transmission materially increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The natural-language content of the skill forces a specific language/locale for user interaction and documentation. Under the policy, this is a violation unless the skill offers a language choice or clearly documents a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file instructs that when the API key is missing, the script will return a Chinese prompt and the agent should relay it verbatim to the user. Combined with repeated directions that messages and outputs are in Chinese, this imposes a specific language on user interactions without presenting an opt-in or alternative locale choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring instructs that messages are for users in Chinese and that the AI should relay them verbatim, and the rest of the CLI help and outputs are likewise hardcoded in Chinese. This is a natural-language locale constraint that applies to all users of the skill, but the file does not provide opt-in or a selectable language mode.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The script is designed to transmit user-supplied patent materials to https://api.ip930.com, which is an external third party. In this context that is security-relevant because patent office actions, claims, and specifications are often highly sensitive, and the transfer happens as part of normal operation.

Content

Scanner excerpt · scripts/patentmax_oa.py (reported line 87)May include surrounding context.

python
# 密钥版(TRIAL_SOURCE 为空):必须设 PATENTMAX_API_KEY。
TRIAL_SOURCE = ""
TRIAL_GROUP = "oa-response"
TRIAL_REGISTER_URL = "https://api.ip930.com/features/api-platform"
KEY_FROM_TRIAL = False

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/patentmax_oa.py (reported line 155)May include surrounding context.

python
if API_KEY:
        return API_KEY
    if not TRIAL_SOURCE:
        die("还没有设置 PatentMax 密钥。到 https://api.ip930.com/features/api-platform 注册(新用户送 ¥50 体验额度)"
            "并创建密钥,设成环境变量 PATENTMAX_API_KEY=pm_live_xxx 后,再运行同一条命令。", EXIT_INPUT)
    API_KEY = _load_trial_key() or _claim_trial_key()
    KEY_FROM_TRIAL = True

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

The --auto mode sets auto_confirm=True, allowing the external system to automatically choose a recommended response strategy and confirm amended claims without a human review checkpoint. In a legal/patent prosecution workflow, this can cause unauthorized substantive filings or user-harmful decisions if an agent enables automation too broadly.

Content

Scanner excerpt · scripts/patentmax_oa.py (reported line 523)May include surrounding context.

python
die("对比文件最多给 10 篇;没给的系统会按通知书里的公开号自己去取。", EXIT_INPUT)
        body["input"]["cited_references"] = [cited_entry(p) for p in args.cited]
    if args.auto:
        body["input"]["auto_confirm"] = True
    if args.title:
        body["input"]["title_hint"] = args.title[:255]

Static analysis

No suspicious patterns detected.