Back to skill

Security audit

夸克扫描王 - 转Office Alibaba-Quark-Transoffice

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does the advertised image-to-document conversion, but it needs Review because it sends sensitive images and an API key to a third-party service with concrete data-handling weaknesses.

Install only if you are comfortable uploading the selected images, scans, or screenshots to Quark's service using your SCAN_WEBSERVICE_KEY. Avoid sensitive documents on shared machines, verify local paths yourself, rotate the API key if exposed, and manually remove generated files from the temp directory after use.

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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/common/ocr_client.py:158
Finding

Cross-Origin Redirect May Disclose the API Key and User Image

Content
View full analysis

Vulnerability Details

File Location: scripts/common/ocr_client.py, lines 158-180
Vulnerability Type: Sensitive data exposure through unrestricted HTTP redirects
Risk Level: Medium

Vulnerable Code:

python
def _build_request_param(self, image_url: str = None, base64_data: str = None) -> Dict[str, Any]:
    """构建请求参数"""
    param = {
        "aiApiKey": self.api_key,
        "dataType": self.data_type,
        "scene": self.scene
    }

    if base64_data:
        param["dataBase64"] = base64_data
    else:
        param["dataUrl"] = image_url

    return param

def _send_request(self, param: Dict[str, Any]) -> requests.Response:
    """发送 HTTP 请求到 OCR API"""
    headers = {"Content-Type": "application/json", "X-Appbuilder-From": "openclaw"}
    response = self.session.post(
        API_URL,
        json=param,
        headers=headers,
        timeout=REQUEST_TIMEOUT,
        allow_redirects=True
    )
    return response

Technical Analysis

The request body contains both the SCAN_WEBSERVICE_KEY credential and either the image Base64 content or its URL. Redirect following is enabled without validating the redirect destination.

An HTTP 307 Temporary Redirect or 308 Permanent Redirect can preserve the original HTTP method and request body. Consequently, if the configured OCR endpoint is compromised, misconfigured, or manipulated into returning a cross-origin redirect, the requests client may retransmit the API key and image payload to the redirected host.

Encoding the image as Base64 is itself necessary for the documented OCR API and is not evidence of covert exfiltration. The vulnerability arises from combining sensitive request-body data with unrestricted redirect handling.

Attack Path

  1. A user invokes the Skill with a private image.
  2. The client creates a JSON request containing the image and SCAN_WEBSERVICE_KEY.
  3. The request is sent t ...[truncated 861 chars]
Remediation
View remediation

Remediation Suggestions

  • Disable automatic redirects by setting allow_redirects=False.
  • If redirects are operationally required, process each redirect manually.
  • Require every redirect destination to use HTTPS.
  • Enforce an exact hostname allowlist before retransmitting the request body.
  • Reject cross-origin redirects and downgrade redirects from HTTPS to HTTP.
  • Prefer transmitting the API key in a dedicated authorization header rather than placing it in the JSON body.
  • Add tests covering 301, 302, 303, 307, and 308 responses, including cross-origin destinations.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/common/validators.py:47
Finding

Symlink and Extension-Only Validation Can Upload Unintended Local Files

Content
View full analysis

Vulnerability Details

File Location: scripts/common/validators.py, lines 47-73; scripts/common/ocr_client.py, lines 139-154
Vulnerability Type: Local file substitution and unintended data disclosure
Risk Level: Medium

Vulnerable Code:

python
class FileValidator:
    """文件验证器"""

    @staticmethod
    def validate(file_path: str) -> Tuple[bool, Optional[str]]:
        """
        验证本地文件

        Args:
            file_path: 文件路径

        Returns:
            (是否有效, 错误信息)
        """
        if not file_path or not isinstance(file_path, str):
            return False, "File path cannot be empty"

        file_path = os.path.expanduser(file_path.strip())

        if not os.path.exists(file_path):
            return False, f"File not found: {file_path}"

        if not os.path.isfile(file_path):
            return False, f"Not a file: {file_path}"

        file_size = os.path.getsize(file_path)
        if file_size > MAX_FILE_SIZE:
            return False, f"File size exceeds {MAX_FILE_SIZE / 1024 / 1024}MB limit"

        ext = os.path.splitext(file_path)[1].lower()
        if ext not in ALLOWED_IMAGE_EXTENSIONS:
            return False, f"File extension '{ext}' not allowed"

        return True, None
python
def _recognize_local_file(self, file_path: str) -> OCRResult:
    """处理本地文件:读取文件并转为 BASE64 后调用 OCR"""
    file_path = os.path.expanduser(file_path.strip())

    is_valid, error_msg = FileValidator.validate(file_path)
    if not is_valid:
        return OCRResult(code="FILE_ERROR", message=f"File validation failed: {error_msg}", data=None)

    try:
        with open(file_path, 'rb') as f:
            file_content = f.read()
        base64_content = base64.b64encode(file_content).decode('utf-8')
    except (IOError, OSError) as e:
        return OCRResult(code="FILE_READ_ERROR", message=f"Failed to read file: {str(e)}", data=None)

    
...[truncated 2057 chars]
Remediation
View remediation

Remediation Suggestions

  • Reject symbolic links using os.path.islink() or equivalent platform-specific checks.
  • Resolve the canonical path and require it to remain inside an explicitly approved input directory.
  • Open files atomically with O_NOFOLLOW where supported.
  • Perform validation against the opened file descriptor rather than validating one path and subsequently reopening it.
  • Verify image magic bytes and decode the content with a trusted image parser before upload.
  • Apply the 5 MB limit while reading from the validated file descriptor.
  • Document and enforce which local directories the Skill is permitted to access.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/common/file_saver.py:172
Finding

Converted Documents Are Written to Shared Temporary Storage Without Explicit Private Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/common/file_saver.py, lines 172-206
Vulnerability Type: Unsafe temporary-file permissions and sensitive-data retention
Risk Level: Low

Vulnerable Code:

python
def save_file_from_base64(
    self,
    base64_content: str,
    extension: str,
    subdir: Optional[str] = None,
    filepath: Optional[str] = None,
    _skip_clean: bool = False
) -> Dict[str, Any]:
    """
    通用的 Base64 文件保存方法

    Args:
        base64_content: BASE64 编码的内容
        extension: 文件扩展名(如 .docx, .xlsx, .png)
        subdir: 子目录名(可选,如 "imgs")
        filepath: 指定文件路径(可选,优先级最高)
        _skip_clean: 内部参数,跳过 base64 清理(已清理过时使用)

    Returns:
        {"code": 0, "msg": "success", "data": {"path": "/path/to/file"}}
    """
    if not _skip_clean:
        base64_content = self._clean_base64(base64_content)
    error = self._validate_base64(base64_content)
    if error:
        logger.error(error)
        return SaveResult.error(error).to_dict()

    if filepath is None:
        filepath = self._generate_filepath(extension, subdir)

    try:
        Path(filepath).parent.mkdir(parents=True, exist_ok=True)
        with open(filepath, 'wb') as f:
            f.write(base64.b64decode(base64_content))
        logger.info(f"文件保存成功:{filepath}")
        return SaveResult.success(filepath).to_dict()
    except (IOError, OSError) as e:
        error_msg = f"保存文件失败:{e}"
        logger.error(error_msg)
        return SaveResult.error(error_msg).to_dict()

The default storage root is defined as:

python
DEFAULT_SAVE_DIR = tempfile.gettempdir()

Technical Analysis

Converted Word, Excel, and PDF documents are stored beneath the system temporary directory. The code creates directories and files using process-default permissions rather than explicitly enforcing private modes.

open(filepath, 'wb') generally requests a mode deri ...[truncated 1275 chars]

Remediation
View remediation

Remediation Suggestions

  • Create a private per-user or per-execution temporary directory with mode 0700.
  • Create output files atomically with mode 0600, using os.open() with O_CREAT | O_EXCL where appropriate.
  • Avoid placing sensitive outputs in a globally shared directory when a private application data directory is available.
  • Implement automatic deletion after the output has been consumed.
  • Provide a configurable retention period and a cleanup command.
  • Ensure cleanup also runs after failures and interrupted executions.
  • Avoid logging full sensitive output paths unless operationally required.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as a narrow image-to-Office/PDF converter, but the documented behavior includes decoding Base64 returned by a remote service and saving it to local disk, which is effectively a generic file-write primitive. If the save path is influenced by inputs or if file type/content is not strictly validated, a malicious or compromised upstream service could cause unexpected files to be written locally.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a narrow image-to-Office/PDF converter, but the documented behavior includes decoding Base64 returned by a remote service and saving it to local disk, which is effectively a generic file-write primitive. If the save path is influenced by inputs or if file type/content is not strictly validated, a malicious or compromised upstream service could cause unexpected files to be written locally.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `SKILL.md` — 本文档(意图分析 + 通用规范)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill explicitly requires environment-variable access and sends user-provided images to an external network service, but it does not declare a restrictive tool scope such as allowed-tools or permissions. That makes the effective capability boundary unclear to reviewers and operators, increasing the chance of unintended execution with broader privileges than necessary.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger condition says the skill should activate even when the user does not explicitly request format conversion, as long as the request involves making image content editable. That broad auto-triggering increases the risk of sending sensitive user images to a third-party service without sufficiently explicit user intent or informed consent for external data transfer.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The save_* methods for Word/Excel/PDF accept a caller-supplied filepath and pass it directly to open() after only creating parent directories. This enables arbitrary file writes anywhere the process has permission, which exceeds the skill's stated role of converting uploaded content into output files under controlled locations and could be abused to overwrite application files, user files, or drop attacker-controlled content in sensitive paths.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest says this skill should be used specifically to convert images/scans into Office or PDF documents and explicitly not for pure text extraction or OCR-style recognition. However, the class and method docstrings describe an OCR client that 'provides image recognition' and returns raw OCR API responses, which is materially broader than format conversion and aligns with text-recognition behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

When a local file path is provided, the code reads the file, base64-encodes its contents, and sends the full content to an external OCR API without any built-in confirmation, minimization, or policy gate. In an agent context, this can cause unintended exfiltration of sensitive local documents if upstream callers pass private files or if users do not realize third-party transfer occurs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This Python file contains only Chinese-language docstrings/comments and a user-facing error message, with no indication that the skill is China/Chinese-specific or that users can choose a language. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module docstring frames this component as being used to save OCR results, implying a narrower OCR-output role. In practice, the code provides a reusable generic file-saving facility for arbitrary Base64 content, including caller-chosen file paths, which is broader than the documented intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The top-level natural-language documentation is provided solely in Chinese, and the class/function docstrings predominantly continue that pattern. This can violate language/locale policy when the skill does not offer a language choice or document that it is intentionally region-specific.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The manifest frames the skill as converting user-provided images, screenshots, or scans into editable documents. Supporting an arbitrary public URL input extends the capability to process remote third-party content, which is not clearly justified by that stated purpose and broadens the skill beyond direct document conversion inputs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This file's user-facing natural-language strings, including the module docstring and argparse description/help text, are written exclusively in Chinese. Under the policy, forcing a specific language without user opt-in can be a locale-policy violation unless the constraint is clearly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code file contains natural-language documentation strings in Chinese only, including the module docstring and function/class descriptions. Under the stated policy, forcing a specific language without user opt-in or a documented locale-specific reason is a language/locale policy issue.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.