Back to skill

Security audit

ocr-passport-xiangyun

Security checks for vulnerabilities and agentic risk

Overview

The skill performs the advertised passport OCR, but it handles very sensitive passport data and API credentials with weak defaults that users should review before installing.

Install only if you are authorized to send the passport images to Xiangyun/netocr and accept that extracted passport data may be written locally. Use --no-save for sensitive documents, avoid shared or synced folders, rotate credentials if they were printed or logged, and consider patching the skill to mask secrets, require upload consent, restrict file permissions, and sanitize spreadsheet exports.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/config_manager.py:37
Finding

Plaintext API credential storage and full credential disclosure

Content
View full analysis
None: """Persist key and secret to config.json.""" data = load_config() data["key"] = key.strip() data["secret"] = secret.strip() with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"[OK] Configuration saved to: {CONFIG_PATH}") ``` ```python if args.command == "load": cfg = load_config() configured = is_configured(cfg) output = { "configured": configured, "key": cfg.get("key", ""), "secret": cfg.get("secret", ""), "config_path": CONFIG_PATH, } print(json.dumps(output, ensure_ascii=False, indent=2)) # Exit code: 0 when configured, 1 otherwise sys.exit(0 if configured else 1) ``` The original source contains Chinese comments and user-facing messages; they are translated above without changing the relevant program behavior. ### Technical Analysis The configuration manager stores the reusable Xiangyun API key and secret in an ordinary plaintext JSON file. The file is opened with the process's default permission behavior, so its final access mode depends on the user's current `umask`. The implementation does not explicitly restrict the file to its owner. The `load` command then prints both credentials in full to standard output. This is unnecessary for determining whether the Skill is configured and can expose credentials through terminal history, captured command output, Agent transcripts, CI logs, or parent processes that collect stdout. In addition, `scripts/config_manager.py:67-68` accepts credentials through `--key` and `--secret` command-line arguments. Depending on the operating system and execution environment, command-line arguments may ...[truncated 1455 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/recognize.py:337
Finding

Passport identity data is cached in plaintext by default

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export.py:115
Finding

Untrusted OCR values can trigger spreadsheet formula injection

Content
View full analysis
dict: """Convert a recognition result into an exportable row.""" row = {} for key, label in FIELDS: row[label] = record.get(key, "") row["Recognition Time"] = record.get( "recognized_at", datetime.now().strftime("%Y-%m-%d %H:%M:%S"), ) row["Status"] = ( "Success" if record.get("success", True) else f"Failure: {record.get('error_message', '')}" ) return row ``` ```python def export_csv(records: list, output_path: str) -> None: rows = [to_row(r) for r in records] if not rows: print("[WARN] No data to export", file=sys.stderr) return fieldnames = list(rows[0].keys()) with open(output_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows) print(f"[OK] Exported CSV: {output_path} ({len(rows)} records)") ``` ```python # Data rows for row_idx, row in enumerate(rows, start=2): for col_idx, name in enumerate(fieldnames, start=1): ws.cell(row=row_idx, column=col_idx, value=row.get(name, "")) ``` The original source contains Chinese labels and messages; they are translated above without changing the relevant program behavior. ### Technical Analysis Values obtained from OCR responses, cached JSON files, batch inputs, directories, or standard input are copied directly into CSV and XLSX cells. No neutralization is applied to strings beginning with spreadsheet formula markers such as `=`, `+`, `-`, or `@`. For XLSX output, a string beginning with `=` can be stored by `openpyxl` as a formula. For CSV output, spreadsheet applications may interpret formula-like cells when the exported file is opened. Depending on the ap ...[truncated 1637 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
scripts/recognize.py:21
Finding

Runtime dependencies are installed without version or integrity pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description focuses on passport OCR, but the skill also performs local caching and export workflows that read and write additional files, including CSV/Excel/JSON generation. This mismatch can mislead users and reviewers about the actual data-handling surface, especially because the data involved is highly sensitive passport PII.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill sends passport images and extracted identity data to a third-party OCR provider but does not present a clear upfront privacy warning or obtain explicit informed consent. Because passport images contain highly sensitive personal data, users may unknowingly transmit regulated PII to an external service, creating privacy, compliance, and data-sovereignty risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The load command prints the stored API secret in cleartext to stdout, which can expose credentials to terminal history, logs, calling processes, or other users with access to captured output. In this skill context, the file manages persistent OCR API credentials, so leaking the secret can enable unauthorized use of the external service and compromise the associated account.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to read local files, write config and result files, and make outbound network requests, but it does not declare any tool scope or permissions boundary. In an agent environment, missing explicit scope increases the chance of overbroad execution against sensitive local files and external services without clear user or platform mediation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill auto-saves OCR results as JSON files next to the source image, but this persistence behavior is not clearly warned about before use. Since the output contains extracted passport fields, silent local caching can expose sensitive identity data to other users, backups, sync tools, or unintended file sharing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation describes sending highly sensitive identity documents (passport images/MRZ data) and API credentials to a third-party OCR service, but provides no privacy, consent, retention, jurisdiction, or data-handling warnings. Because passports contain regulated PII and identity data, normalizing this transfer without explicit safeguards materially increases the risk of privacy violations, unauthorized processing, and compliance failures.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

This example performs an outbound POST to a third-party endpoint with a passport image file plus API key and secret. Although this is the intended API usage, it still represents a real security/privacy risk in the skill context because it transfers extremely sensitive identity data off-platform to an external service without accompanying guardrails or disclosure.

Content

Scanner excerpt · references/api_docs.md (reported line 129)May include surrounding context.

md
# 文件上传方式(推荐)
with open("passport.jpg", "rb") as f:
    resp = requests.post(
        "https://netocr.com/api/recog.do",
        files={"file": ("passport.jpg", f)},
        data={

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This example base64-encodes and transmits the full passport image to an external OCR endpoint together with credentials. Base64 is only an encoding, not protection, so the same sensitive passport data is exposed to third-party processing; in this skill context, that creates substantial privacy and compliance risk.

Content

Scanner excerpt · references/api_docs.md (reported line 150)May include surrounding context.

md
with open("passport.jpg", "rb") as f:
    img_b64 = base64.b64encode(f.read()).decode("utf-8")

resp = requests.post(
    "https://netocr.com/api/recogliu.do",
    data={
        "img": img_b64,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The natural-language strings in the module docstring and CLI descriptions are entirely in Chinese, which imposes a language choice on users without opt-in. The file does not document that the skill is intentionally limited to a Chinese-speaking or region-specific audience, so this appears to violate the language/locale policy.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The top-level docstring says python config_manager.py reset clears the configuration with '需二次确认' (requires secondary confirmation). However, the reset subcommand is defined without any confirmation flag or prompt, and reset_config() is called directly when invoked. This is an active contradiction between the documented intent and the code's behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill sends passport images and related credentialed requests to a third-party OCR service, which involves transmitting extremely sensitive personal data off-host. Without a clear user-facing warning and consent flow, users may unknowingly disclose passport information to an external processor, creating privacy, compliance, and data handling risks.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script automatically persists OCR output derived from passport images to a local JSON file by default, even though the skill description primarily frames behavior as recognition/extraction. Because passport OCR output contains highly sensitive identity data, implicit storage increases the risk of unintended retention, later disclosure, backup propagation, and access by other local users or processes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code writes OCR results containing passport-derived PII to a local JSON file by default, without an explicit warning at runtime. This creates a privacy and data minimization problem because highly sensitive identity data may persist on disk longer than intended and be accessible through filesystem access, backups, sync tools, or later reuse.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The natural-language documentation and command descriptions are entirely in Chinese, and the script presents user-facing messages only in Chinese. There is no indication that the skill is intentionally limited to Chinese-speaking users or that another language option is available, which may violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The module documentation states results are printed to stdout for downstream use, but the implementation also saves OCR results locally without clearly disclosing that side effect. This mismatch can mislead operators into handling sensitive passport data under incorrect assumptions, increasing the chance of accidental retention and exposure.

Content

No source excerpt is available for this finding.