Back to skill

Security audit

Enterprise Qualification Ocr

Security checks for vulnerabilities and agentic risk

Overview

This OCR skill has a legitimate purpose and discloses remote processing, but it can upload any readable local file and can be configured to send documents and the API token to an arbitrary endpoint.

Review before installing. Only use this skill for documents you are authorized to send to Scnet, verify config/.env points to the intended HTTPS api.scnet.cn endpoint, and do not invoke it on arbitrary local paths. Prefer running it in a restricted workspace with only the target document accessible.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:78
Finding
Configurable API Endpoint Can Expose the Bearer Token and Uploaded Document<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:78-112` **Vulnerability Type**: Unrestricted sensitive-data transmission destination **Risk Level**: Medium ### Vulnerable Code ```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 识别函数。 当遇到 429 (Too Many Requests) 时,自动等待后重试。 调用 Scnet OCR API 进行识别""" api_base = config['SCNET_API_BASE'] api_key = config['SCNET_API_KEY'] url = f"{api_base}/ocr/recognize" # 检查文件是否存在 if not os.path.isfile(file_path): sys.exit(f"错误: 文件不存在 - {file_path}") # 自动检测 MIME 类型 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) ``` ### Technical Analysis The Skill reads `SCNET_API_BASE` from its configuration and uses it directly to construct the request URL. It does not parse or validate the URL, require HTTPS, or verify that the destination hostname is `api.scnet.cn`. The same request transmits two sensitive assets: 1. The Scnet API key in the `Authorization: Bearer` header. 2. The complete user-selected document in a multipart upload. Although remote transmission is necessary for the declared OCR functionality and is disclosed in `SKILL.md`, permitting an arbitrary destination is not necessary when the declared service endpoint is fixed. Any party capable of changing `config/.env` can redirect both assets to a server under its control. ### ...[truncated 1234 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Hard-code the approved service base URL if alternate endpoints are not a functional requirement. 2. If endpoint configuration must remain available: - Parse it with `urllib.parse.urlparse`. - Require the `https` scheme. - Require an exact allowlisted hostname such as `api.scnet.cn`. - Reject embedded credentials, unexpected ports, fragments, and malformed URLs. 3. Avoid sending a Scnet credential to custom endpoints. Custom providers should use separate provider-specific credentials. 4. Display the validated destination and require explicit user approval before transmitting sensitive documents when the destination differs from the default. 5. Add automated tests covering HTTP URLs, deceptive subdomains, user-info URL syntax, redirects, and malformed hostnames. 6. Consider disabling redirects or validating every redirect target so an approved endpoint cannot redirect credentials and files to an unapproved host. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/main.py:90
Finding
Insufficient File Validation Permits Upload of Arbitrary Readable Local Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.py:90-112` **Vulnerability Type**: Overly broad local-file access and upload **Risk Level**: Medium ### Vulnerable Code ```python # 检查文件是否存在 if not os.path.isfile(file_path): sys.exit(f"错误: 文件不存在 - {file_path}") # 自动检测 MIME 类型 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) ``` The caller-controlled path is obtained without further restriction: ```python ocr_type = sys.argv[1] file_path = sys.argv[2] config = load_config() # 调用带重试的识别函数 recognize_with_retry(ocr_type, file_path, config) ``` ### Technical Analysis The script only verifies that `os.path.isfile(file_path)` returns true. It does not restrict the path to an authorized directory, reject symbolic links, validate the file extension or signature, enforce a size limit, or confirm that the selected file is an image or PDF. `mimetypes.guess_type()` relies primarily on the filename and does not inspect file contents. Unknown files are not rejected; they are explicitly uploaded as `application/octet-stream`. Consequently, any file readable by the process can be transmitted, even when it is unrelated to OCR. This exceeds the minimum local-file access required for the declared functionality, which is limited to user-authorized qualification documents and supported OCR formats. ### Attack Path 1. An attacker-controlled instruction, automation layer, or mistaken invocation supplies the path of a sensitive local file instead of an intended image or PDF. 2. `os.path.isfile()` accepts the path, inc ...[truncated 978 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict uploads to explicitly supported formats such as JPEG, PNG, and PDF. 2. Validate both the extension and file signature; do not trust `mimetypes.guess_type()` alone. 3. Reject unknown content instead of assigning `application/octet-stream`. 4. Resolve the path with `Path.resolve()` and require it to fall within an approved user-selected directory or attachment workspace. 5. Reject symbolic links or safely open files using platform controls that prevent symlink following. 6. Require explicit approval for the exact resolved path before network transmission. 7. Enforce conservative file-size and page-count limits before opening or uploading content. 8. Validate `ocr_type` against the documented enumeration before sending the request. 9. Run the Skill in a sandbox with access only to the document selected by the user, rather than the user's broader filesystem. 10. Log only non-sensitive metadata and never include document contents or credentials in diagnostic output. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:83
Finding
Unpinned Dependency Installation Creates a Non-Reproducible Supply-Chain Boundary<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:83-88` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```markdown ### 依赖安装 本技能需要 Python 3.6+ 和 requests 库。请运行以下命令: ```bash pip install requests ``` ``` ### Technical Analysis The installation instruction retrieves whichever `requests` release and transitive dependencies the configured Python package index resolves at installation time. The project does not provide a version constraint, lockfile, package hashes, or an isolated-environment requirement. This makes installations non-reproducible and prevents users from confirming that they are installing the same reviewed dependency set. The package name itself is legitimate and there is no evidence that the project intentionally references a malicious or typosquatted package. The risk arises from unsafe dependency management rather than a confirmed malicious dependency. ### Attack Path 1. A user follows the documented `pip install requests` command. 2. `pip` contacts the user's configured package index or mirror. 3. The index resolves an unreviewed future version or altered transitive dependency. 4. The package is installed into the selected Python environment without hash verification. 5. Malicious or compromised installation/runtime code could execute with the user's privileges or alter the Skill's HTTP behavior. Successful exploitation requires a compromised package release, compromised package index or mirror, unsafe index configuration, or another supply-chain failure. ### Impact Assessment A compromised dependency could execute with the privileges of the user performing installation and subsequently access the same files, environment, network, and API credential available to the Skill. If installation occurs in a privileged or shared Python environment, the effect could extend beyond this project. Under ordinary installation from the official Python Package Index, this ...[truncated 97 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `requests` to a reviewed version or narrowly reviewed version range. 2. Provide a dependency lockfile containing resolved transitive versions. 3. Supply and verify cryptographic hashes, for example through a hash-locked requirements file. 4. Recommend installation with `python -m pip` inside a dedicated virtual environment. 5. Document the intended trusted package index and avoid untrusted extra indexes. 6. Add automated dependency vulnerability and integrity scanning. 7. Periodically review and update pinned versions rather than resolving arbitrary latest releases during installation. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Credential Access

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

# --- 新增:重试配置 ---
MAX_RETRIES = 3            # 最大重试次数
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# --------------------

def load_config():
    """从 .env 文件加载配置,若文件不存在则抛出友好错误"""
    if not ENV_FILE.exists():
        error_msg = (
            "\n===============================================\n"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This markdown file uses Chinese throughout, including the title and feature descriptions, with no indication that the skill is region-specific or that users can opt into this locale. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
* [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)
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.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documentation indicates capabilities to read local files, invoke Python from the shell, and send image data to a remote API, but it does not declare an explicit tool scope such as permissions or allowed-tools. That omission weakens enforcement and user visibility around what the skill may access, especially because it processes sensitive enterprise certificate images and transmits them off-device.

External Transmission

Medium
Category
Data Exfiltration
Content
SCNET_API_KEY=your_scnet_api_key_here

# API 基础地址(一般无需修改)
SCNET_API_BASE=https://api.scnet.cn/api/llm/v1
```
2. 添加:`SCNET_API_KEY=你的密钥`
3. 设置文件权限为 600(仅所有者可读写)
Confidence
96% confidence
Finding
The skill is explicitly configured to send uploaded certificate images to `api.scnet.cn`, which is a real external transmission of sensitive enterprise identity data. Because the documents include identifiers, addresses, legal representatives, and registration details, external transfer materially increases confidentiality and compliance risk if users are not tightly informed and protected.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The auto-trigger guidance says the AI will automatically invoke the skill based on broad description keywords, without strong activation boundaries or an explicit consent gate. In this context, that can cause accidental OCR of local documents and unintended upload of highly sensitive business registration materials to a third-party service.

External Transmission

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

### 输出
Confidence
95% confidence
Finding
This finding reinforces that the configured API base points to an external service endpoint, meaning the skill depends on remote processing for sensitive document contents. In the context of business qualification certificates, such outbound transfer is significant because it can expose regulated or commercially sensitive records beyond the local environment.

External Transmission

Medium
Category
Data Exfiltration
Content
# Sugon-Scnet OCR API 文档摘要

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

## 请求头
- `Content-Type: multipart/form-data`
Confidence
88% confidence
Finding
The skill directs users to transmit uploaded files to an external OCR service endpoint, which creates a real data egress path for potentially sensitive business-license and registration documents. In this skill context, the risk is elevated because the listed document types routinely contain identifiers, addresses, legal representative names, and other sensitive organizational information.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation instructs users to upload images, PDFs, or archives containing enterprise qualification documents to a third-party OCR endpoint, but provides no privacy, retention, consent, or data-handling warning. Because these documents commonly contain sensitive corporate and personal data, omission of handling guidance can lead to unintended external disclosure and compliance issues.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
L039-L045 和 L148-L152 的错误提示明确告诉用户可通过环境变量 export SCNET_API_KEY 来配置凭据,并将其标注为推荐方式。但 load_config 在 L050-L079 仅解析 config/.env 文件,从未读取 os.environ,因此文档化的配置方式与实际行为相矛盾。

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"   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)
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"   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)
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
)
        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):
Confidence
83% confidence
Finding
The skill transmits user-supplied files, which may contain sensitive enterprise registration documents and personal/business data, to an external third-party API endpoint. In an OCR skill this behavior is expected, but it still creates real privacy and data-handling risk because highly sensitive documents leave the local trust boundary.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The manifest description is entirely in Chinese and gives no indication that users may interact in other languages or choose a locale. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The manifest description forces a specific language presentation for invocation and usage context, but the file does not offer the user a language choice or explain that the skill is intentionally limited to Chinese-speaking users or a China-specific deployment. This can violate language/locale policy when no opt-in or justification is provided.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
A language or locale policy issue exists when a skill or its instructions force a specific language without user choice or justification. This file presents all operational documentation in Chinese and does not indicate that the language is optional, selectable, or region-specific.

Static analysis

No suspicious patterns detected.