Back to skill

Security audit

expense_voucher_ocr

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed OCR uploader for financial documents, but it allows its upload destination to be changed without validation, which could send documents and the API key to the wrong server.

Install only if you are comfortable sending the selected invoice, receipt, travel, or medical billing file to Scnet's cloud OCR service. Before use, keep config/.env owner-readable only, verify SCNET_API_BASE is exactly the intended Scnet HTTPS API base, and do not process documents whose confidentiality rules forbid third-party upload.

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

Error
Location
scripts/main.py:78
Finding

Configurable OCR Endpoint Enables Credential and Sensitive Document Exfiltration

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py, lines 78-112
Vulnerability Type: Unrestricted sensitive-data destination
Risk Level: High

The application permits SCNET_API_BASE to be supplied through the local configuration without validating its scheme or hostname. It then sends both the Scnet API key and the complete user-selected document to the resulting URL.

python
config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
return config

def recognize_with_retry(ocr_type, file_path, config, retry_count=0):
    """
    OCR recognition function with retry behavior.
    """
    api_base = config['SCNET_API_BASE']
    api_key = config['SCNET_API_KEY']
    url = f"{api_base}/ocr/recognize"

    # Check whether the file exists
    if not os.path.isfile(file_path):
        sys.exit(f"Error: file does not exist - {file_path}")

    # Automatically detect the MIME type
    mime_type, _ = mimetypes.guess_type(file_path)
    if mime_type is None:
        mime_type = 'application/octet-stream'

    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
            )
    except Exception as e:
        sys.exit(f"Network request failed: {str(e)}")

Technical Analysis

Cloud OCR legitimately requires transmitting the selected document and an authentication credential to the service provider. However, the minimum privilege required by the declared functionality is network access to the docu ...[truncated 2406 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove runtime endpoint configurability when only the official service is supported. Define the complete OCR URL as a trusted constant:

    python
    OCR_URL = "https://api.scnet.cn/api/llm/v1/ocr/recognize"
    
  2. If custom endpoints are operationally necessary, require an explicit opt-in and validate the parsed URL before transmitting any credential or file:

    • Require the https scheme.
    • Allowlist approved hostnames, normally only api.scnet.cn.
    • Reject embedded usernames or passwords.
    • Reject fragments and unexpected query strings.
    • Restrict ports to approved values.
    • Normalize and verify the final path.
  3. Disable automatic redirects for this sensitive upload with allow_redirects=False, or independently validate every redirect destination before resending credentials or file content.

  4. Separate custom endpoint credentials from SCNET_API_KEY. Never send a production Scnet token to an endpoint that is not verified as belonging to Scnet.

  5. Display the validated destination and require explicit confirmation before the first upload when nonstandard endpoints are permitted.

  6. Add automated tests confirming rejection of HTTP URLs, lookalike domains, embedded credentials, unapproved ports, malformed URLs, and attacker-controlled hosts.

  7. Restrict config/.env permissions to the owning user and document configuration-integrity requirements. If tampering is suspected, rotate the API key and review previously processed documents for potential exposure.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (18)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/main.py (reported line 20)May include surrounding context.

python
# 获取技能根目录(脚本所在目录的上一级)
SKILL_ROOT = Path(__file__).parent.parent.absolute()
ENV_FILE = SKILL_ROOT / "config" / ".env"

# --- 新增:重试配置 ---
MAX_RETRIES = 3            # 最大重试次数

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 63)May include surrounding context.

md
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 127)May include surrounding context.

md
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/main.py (reported line 29)May include surrounding context.

python
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 41)May include surrounding context.

md
* [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
* [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
* [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
* [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill performs sensitive actions implied by its documentation: reading arbitrary local files, invoking shell/Python execution, and uploading file contents to an external API, yet it declares no explicit tool scope or permission boundaries. Without a declared allowlist, an agent platform may expose broader-than-necessary capabilities or fail to present clear user consent for local file access and network exfiltration.

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 manifest and README-style instructions is presented only in Chinese, including setup, warnings, and usage guidance. This effectively forces a specific language on users without opt-in or justification, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
85% confidence
Finding

This skill is explicitly designed to transmit local invoice and receipt files to api.scnet.cn, which constitutes external data exfiltration of potentially highly sensitive financial and personal information. While documented and apparently intentional, it remains security-relevant because local files may include tax IDs, bank details, travel records, and medical billing data.

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

SCNET_API_KEY=your_scnet_api_key_here

API 基础地址(一般无需修改)

SCNET_API_BASE=https://api.scnet.cn/api/llm/v1

text
2. 添加:`SCNET_API_KEY=你的密钥`
3. 设置文件权限为 600(仅所有者可读写)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 107)May include surrounding context.

md
| 变量名 | 默认值 | 说明 |
|--------|--------|------|
| SCNET_API_KEY | 必需 | Scnet API 密钥 |
| SCNET_API_BASE | https://api.scnet.cn/api/llm/v1 | API 基础地址(一般无需修改) |

### 输出

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The file documents direct transmission of uploaded documents to an external domain, which creates a real data-exfiltration boundary even if the service itself is legitimate. In the context of expense-voucher OCR, the uploaded content commonly includes PII, tax data, and banking information, so sending it to an external provider materially increases privacy and compliance risk unless clearly controlled and disclosed.

Content

Scanner excerpt · references/api-docs.md (reported line 4)May include surrounding context.

md
# Sugon-Scnet OCR API 文档摘要

## 接口地址
`POST https://api.scnet.cn/api/llm/v1/ocr/recognize`

## 请求头
- `Content-Type: multipart/form-data`

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation instructs clients to upload receipt, invoice, and other financial document images to a third-party OCR endpoint but does not disclose that potentially sensitive financial and personal data will be transmitted off-platform. In a skill centered on expense voucher OCR, this omission is especially risky because users are likely to submit invoices, tax records, and transport receipts containing regulated or confidential information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The sample response exposes a wide range of sensitive fields such as taxpayer identifiers, bank account details, invoice metadata, and names, yet the documentation provides no warning about secure handling of OCR results. Because this skill specifically processes financial documents, downstream consumers may inadvertently log, display, or persist highly sensitive extracted data without proper controls.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/main.py (reported line 45)May include surrounding context.

python
"   b) 配置文件:\n"
            f"      mkdir -p {SKILL_ROOT}/config\n"
            f"      echo 'SCNET_API_KEY=你的密钥' > {ENV_FILE}\n"
            f"      chmod 600 {ENV_FILE}\n"
            "\n配置完成后重新运行。"
        )
        sys.exit(error_msg)

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/main.py (reported line 152)May include surrounding context.

python
"   b) 配置文件:\n"
            f"      mkdir -p {SKILL_ROOT}/config\n"
            f"      echo 'SCNET_API_KEY=你的密钥' > {ENV_FILE}\n"
            f"      chmod 600 {ENV_FILE}\n"
            "\n配置完成后重新运行。"
        )
        sys.exit(error_msg)

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The script uploads user-supplied document files to an external OCR service at api.scnet.cn, which is a genuine outbound data transmission risk. Because the skill processes financial invoices and receipts, the transmitted content may contain sensitive personal, financial, or corporate data, making this more sensitive than a generic API call.

Content

Scanner excerpt · scripts/main.py (reported line 78)May include surrounding context.

python
)
        sys.exit(error_msg)

    config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
    return config

def recognize_with_retry(ocr_type, file_path, config, retry_count=0):

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The description is entirely in Chinese, which imposes a language choice in user-facing metadata without any indication of multilingual support or user opt-in. Under the policy, locale or language constraints should either be optional for the user or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file is entirely written in Chinese, including the title and feature descriptions, with no indication that this is a region-specific skill or that users can opt into the language. Under the stated policy, forcing a specific language without user choice can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The markdown content is entirely written in Chinese and does not indicate that language selection is optional or that the skill is intentionally limited to a Chinese-language or region-specific context. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.