Back to skill

Security audit

jf-open-pro-ptz-control

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to do what it claims, but it controls real cameras using sensitive credentials and allows the API endpoint to be redirected without validation.

Install only if you trust the publisher and will restrict use to authorized camera operators. Configure JF_ENDPOINT only to an official documented host such as api-cn.jftechws.com, api-as.jftechws.com, api-eu.jftechws.com, or api-na.jftechws.com, and avoid environments where another party can alter variables. Treat clear-preset and clear-tour as destructive device-configuration actions and confirm intent outside the skill before running them.

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/ptz_control.py:30
Finding

Unrestricted API Endpoint Allows Disclosure of Device Credentials and Authenticated Requests

Content
View full analysis

Vulnerability Details

File Location: scripts/ptz_control.py, lines 30–31, 39–48, and 59–86; the same request pattern is repeated at lines 108–132, 154–179, 202–229, 249–254, and 271–276.
Vulnerability Type: Unvalidated network destination for sensitive authenticated requests
Risk Level: High

Vulnerable Code

python
# API base address (switchable through an environment variable)
JF_ENDPOINT = os.getenv("JF_ENDPOINT", "api-cn.jftechws.com")
JF_BASE_URL = f"https://{JF_ENDPOINT}/gwp/v3"


def get_headers(uuid: str, app_key: str, app_secret: str, move_card: int) -> Dict[str, str]:
    """Generate request headers containing the signature and timestamp."""
    time_millis = get_time_millis()
    signature = generate_signature(uuid, app_key, app_secret, time_millis, move_card)

    return {
        "Content-Type": "application/json; charset=UTF-8",
        "uuid": uuid,
        "appKey": app_key,
        "timeMillis": time_millis,
        "signature": signature,
        "X-Request-Id": os.urandom(16).hex()
    }


def ptz_direction(device_token: str, uuid: str, app_key: str,
                  app_secret: str, move_card: int,
                  direction: str, command: str,
                  channel: int = 0, step: int = 5) -> Dict[str, Any]:
    url = f"{JF_BASE_URL}/rtc/device/opdev/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)

    direction_map = {
        "up": "DirectionUp",
        "down": "DirectionDown",
        "left": "DirectionLeft",
        "right": "DirectionRight",
        "leftup": "DirectionLeftUp",
        "leftdown": "DirectionLeftDown",
        "rightup": "DirectionRightUp",
        "rightdown": "DirectionRightDown"
    }

    body = {
        "Name": "OPPTZControl",
        "OPPTZControl": {
            "Command": direction_map.get(direction.lower(), direction),
            "Parameter": {
               
...[truncated 3295 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace unrestricted endpoint configuration with an explicit allowlist containing only the documented service hosts:

    • api-cn.jftechws.com
    • api-as.jftechws.com
    • api-eu.jftechws.com
    • api-na.jftechws.com
  2. Parse and validate endpoint configuration before creating any request. Reject:

    • Unknown hostnames
    • Embedded usernames or passwords
    • URL paths or query strings
    • Non-default ports
    • IP-address literals
    • Invalid or ambiguous hostname syntax
  3. Prefer a region selector such as CN, AS, EU, or NA, then map that value internally to a fixed trusted hostname. This avoids accepting a raw network destination.

  4. Keep TLS certificate verification enabled and consider certificate or public-key pinning if supported by the deployment environment.

  5. If the upstream API supports it, transmit the device token in an authorization header rather than in the URL path. Ensure credentials and signed headers are redacted from application, proxy, and diagnostic logs.

  6. Use short-lived, device-scoped tokens with only the PTZ permissions needed for the requested operation. Revoke and rotate any token suspected of having been sent to an untrusted endpoint.

  7. Add automated tests confirming that unsupported hosts, ports, paths, credentials, and malformed endpoint values are rejected before any network request occurs.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (24)

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The request target is derived from the JF_ENDPOINT environment variable and then used to build authenticated HTTPS requests that include uuid, appKey, timestamp, and signature headers. If an attacker can influence the environment, the skill will transmit signed device-control requests and metadata to an arbitrary host, creating an SSRF/exfiltration channel and potentially enabling unauthorized remote control workflows against attacker-controlled infrastructure.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 86)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

This network call uses a URL built from an environment-controlled endpoint and sends authenticated zoom/focus control commands externally. In a hostile runtime, an attacker could redirect these signed requests to infrastructure they control, exposing request metadata and abusing the skill as a trusted outbound client.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 132)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

Preset-management requests are sent to a URL influenced by environment input, and those requests carry authentication headers plus operational data such as preset names and identifiers. That makes the skill susceptible to exfiltration or SSRF-style misuse when deployed in an environment where variables can be tampered with.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 179)May include surrounding context.

python
if command in ["set", "set-name"] and preset_name:
        body["OPPTZControl"]["Parameter"]["PresetName"] = preset_name
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

Tour-control operations are posted to a destination assembled from JF_ENDPOINT, so an attacker who controls that environment variable can redirect authenticated remote-control traffic. Because these commands affect physical camera behavior, misuse extends beyond data exposure to potentially unauthorized operational actions or replay opportunities.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 229)May include surrounding context.

python
body["OPPTZControl"]["Parameter"]["Preset"] = preset
        body["OPPTZControl"]["Parameter"]["Step"] = step
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The preset-list retrieval call sends signed headers to an environment-derived host, which can leak authentication-related values and device identifiers to an attacker-controlled server. Even though this operation is read-oriented, the exposure of trusted request material and the ability to induce outbound connections make it security-relevant.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 254)May include surrounding context.

python
body = {"Name": "Uart.PTZPreset"}
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tainted flow: 'url' from os.getenv (line 271, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The tour-configuration retrieval request also uses a URL assembled from untrusted environment input, enabling redirection of signed outbound traffic. In this skill context, the requests are for a physical security/camera control platform, so endpoint tampering is more dangerous because it can expose operational metadata and trust relationships around device management.

Content

Scanner excerpt · scripts/ptz_control.py (reported line 276)May include surrounding context.

python
body = {"Name": "Uart.PTZTour"}
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

声明描述的核心能力是控制云台摄像头设备,但提供的代码块仅实现了 API 加密辅助功能:生成 20 位时间戳、字符串转字节、自定义字节移位、合并字节数组,以及根据 uuid/app_key/app_secret/time_millis 计算签名。这些行为属于接口认证/请求签名支持逻辑,而不是云台方向控制、变倍聚焦、预置位管理或巡航计划执行。虽然这类加密模块可能是设备控制系统的配套组件,但就该代码块本身而言,其实际主要用途与声明的技能功能明显不一致。

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 372)May include surrounding context.

md
| `SKILL.md` | 技能文档 |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| `scripts/ptz_control.py` | Python 执行脚本 |

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill documents execution steps that rely on environment variables and outbound network access, but it does not declare an explicit tool/permission scope. This creates a governance gap: a host may grant broader capabilities than users expect, and reviewers cannot easily verify whether network and secret access are intentionally limited to the minimum required.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill controls camera orientation, zoom, presets, and automated patrols but does not warn users about privacy, surveillance, or real-world operational effects. Missing these warnings can lead to unsafe or inappropriate use in sensitive spaces, and users may not realize commands can alter what is being monitored or recorded.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger phrases are broad and overlap with common language such as directional words, zoom, focus, presets, and patrol-related commands. In a voice or intent-routed environment, this increases the chance of accidental invocation, which is especially risky because the skill controls physical camera movement and monitoring behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file includes its module docstring and user-facing runtime note entirely in Chinese, and there is no indication that the skill is region-specific or that users can opt into a preferred language. The policy explicitly flags language or locale constraints when they are imposed without user choice or clear justification.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 86)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 132)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 179)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 229)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 254)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/ptz_control.py (reported line 276)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code invokes a remote clear/delete preset action that changes device state irreversibly, but it only prints progress text and proceeds immediately. There is no confirmation prompt, cautionary comment/docstring warning, or other user disclosure indicating this action deletes an existing preset.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This action sends a remote command to clear a patrol/tour line, which can remove existing configuration from the device. The function prints status messages but does not provide a cautionary disclosure or confirmation before carrying out the destructive operation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

文档标题、描述、触发词和使用说明均固定为中文,未说明这是面向特定中文用户群的受限技能,也未提供其他语言选择或用户选择机制。按照语言/locale 政策,强制单一语言而无用户选择可能构成自然语言策略违规。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The printed notice at runtime is natural-language output shown to the user, but it is hardcoded in Chinese only. Because the file does not offer language selection or explain that the tool is intentionally restricted to a Chinese-speaking context, this is a language-policy concern.

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 description is exclusively in Chinese, which effectively forces a specific language for users without indicating opt-in or a justified region-specific constraint. Under the stated policy, locale-specific language should be optional or explicitly documented as intentional and justified.

Content

No source excerpt is available for this finding.