Back to skill

Security audit

中文快递追踪

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Chinese courier-tracking skill that uses a third-party tracking API and local saved tracking list, with privacy and permission hardening improvements advisable.

Before installing, understand that shipment numbers may be sent to Kuaidi100 and saved locally with recent status details. Avoid using it for highly sensitive shipments unless you are comfortable with that sharing and local retention, and clear saved entries when no longer needed.

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/express_tracker.py:34
Finding

Shipment Tracking Data Stored Without Explicit Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/express_tracker.py, lines 11-12, 34-41, and 164-171
Vulnerability Type: Plaintext sensitive-data storage with process-default permissions
Risk Level: Medium

Vulnerable Code

python
DATA_DIR = os.path.expanduser("~/.qclaw/skills/cn-express-tracker/data")
DATA_FILE = os.path.join(DATA_DIR, "express.json")
python
def load():
    os.makedirs(DATA_DIR, exist_ok=True)
    if os.path.exists(DATA_FILE):
        return json.load(open(DATA_FILE))
    return {"tracking": []}


def save(d):
    with open(DATA_FILE, "w") as f:
        json.dump(d, f, ensure_ascii=False, indent=2)
python
        existing = [i for i in data["tracking"] if i["number"] == num]
        if existing:
            existing[0].update({"carrier": carrier, "last_status": last_s, "last_time": last_t})
            save(data)
            return f"♻️ 更新 {num}({carrier})\n📍 {last_s}"
        data["tracking"].append({"number": num, "carrier": carrier, "last_status": last_s, "last_time": last_t})
        save(data)
        return f"✅ 已添加\n━━━━━━━━━━━━━━\n📦 {num}\n🏢 {carrier}\n📍 {last_s}"

Technical Analysis

The application persists tracking numbers, carrier identifiers, shipment status text, and timestamps in a plaintext JSON file. These values can reveal sensitive logistics information, including package identifiers and recent shipment activity.

The storage directory is created with os.makedirs() without an explicit owner-only mode, while the data file is created through open(..., "w") without an explicit permission mode. Consequently, effective permissions depend on the runtime environment's umask and any pre-existing directory or file permissions. Under a permissive umask, the directory or JSON file may be readable by other local accounts.

The implementation also does not verify that an existing file is owned by the expected user, reject sym ...[truncated 1332 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the storage directory with owner-only permissions and correct permissions on existing directories:

    python
    os.makedirs(DATA_DIR, mode=0o700, exist_ok=True)
    os.chmod(DATA_DIR, 0o700)
    
  2. Create the data file with mode 0600 by using os.open() rather than relying on process-default permissions:

    python
    flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
    fd = os.open(DATA_FILE, flags, 0o600)
    with os.fdopen(fd, "w", encoding="utf-8") as f:
        json.dump(d, f, ensure_ascii=False, indent=2)
    
  3. Validate existing storage before reading or writing:

    • Confirm that the file is owned by the current user.
    • Reject symbolic links using os.lstat() or platform-supported no-follow flags.
    • Reject unexpected file types.
    • Correct permissions to 0600 when safe to do so.
  4. Use an atomic write strategy:

    • Securely create a temporary file in the same directory with mode 0600.
    • Write and flush the complete JSON document.
    • Optionally call os.fsync().
    • Atomically replace the destination using os.replace().
  5. Minimize persisted information. Store only fields required for functionality and provide a clear retention or deletion mechanism for shipment data.

  6. Update the privacy documentation to state that tracking numbers are sent to Kuaidi100 for lookup and that results are stored locally in plaintext with owner-only access.

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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill documentation advertises behaviors that require network access and local file writes, but it does not declare any explicit tool scope or permissions boundary. This creates a transparency and least-privilege problem: users and the hosting agent may invoke a skill with broader effective capabilities than the manifest communicates, increasing the chance of unintended data transmission or persistence.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The description does not clearly warn that shipment numbers will be sent to a third-party tracking service and stored in a local JSON file. Tracking numbers can be sensitive personal or business metadata, so undisclosed external sharing and retention undermine informed consent and create privacy risk.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases include very common words such as '快递', '物流', and '单号', which can appear in normal conversation unrelated to an intentional tool invocation. Overbroad triggers can cause accidental activation, leading to unexpected shipment lookups, local state changes, or external disclosure of tracking numbers without clear user intent.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill claims to provide courier-status lookup, but it also persists users' tracking numbers locally in a per-user data file. Tracking numbers can reveal purchase activity and logistics metadata, so undisclosed storage expands the privacy and data-retention risk beyond what users would reasonably expect from a simple query tool.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code sends tracking numbers to kuaidi100.com, an external third-party service, without any in-band notice or consent flow. Tracking numbers can be personal or commercially sensitive and may allow correlation of deliveries, so silent transmission creates a privacy and data-sharing risk in this skill context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The clear-all command irreversibly deletes all saved tracking entries immediately based on keyword matching alone. In a chat-driven skill, accidental triggering or ambiguous input can cause unintended data loss, especially because no confirmation step or undo capability exists.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The natural-language description, examples, and trigger instructions are entirely in Chinese, which effectively forces a specific language without user opt-in. The file does not indicate that the skill is intentionally limited to a Chinese-speaking or region-specific audience in a way that would justify the restriction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill description and all user-facing strings are written only in Chinese, indicating an implicit language constraint. There is no natural-language indication that users may choose another language or that the locale restriction is intentional and documented as region-specific.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

函数文档和注释在 L098、L105、L110 表示发生 SSL 失败时会进行兼容性回退,但 L112 的第二次 requests.get 仍然设置 verify=True,与第一次请求相同。这里不是信息缺失,而是注释对实际行为的直接误述。

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

L148-L159 支持列出全部已保存的追踪记录及其上次状态,这是一种本地追踪管理能力,而不是单次自动识别并查询物流状态的实现细节。该能力虽不明显恶意,但与描述中的狭义查询助手存在语义偏差。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.