Back to skill

Security audit

jf-open-pro-ptz-control

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed PTZ camera-control tool, but it gives high-impact camera movement and patrol authority with broad trigger phrasing and an unvalidated endpoint override.

Install only for authorized operators of the target JF PTZ devices. Keep JF_ENDPOINT set to one of the documented vendor regional hosts, protect JF_APP_SECRET and JF_DEVICE_TOKEN, and require explicit confirmation before movement, preset deletion, tour start, or tour clearing actions.

Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

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

Critical
Category
Data Flow
Content
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:
Confidence
83% confidence
Finding
The request destination is derived from JF_ENDPOINT, which is read from the environment and interpolated directly into the base URL. If an attacker can influence environment variables, they can redirect authenticated PTZ requests and signed headers to an attacker-controlled host, enabling SSRF-like behavior and credential/signature exposure. In this skill context, the transmitted data is especially sensitive because it includes device control operations and authentication material.

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

Critical
Category
Data Flow
Content
}
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:
Confidence
83% confidence
Finding
The POST target is built from an environment-controlled endpoint and used for zoom/focus control requests without validation. An attacker who can set JF_ENDPOINT could cause signed control traffic to be sent to an arbitrary external server, exposing headers and enabling unauthorized outbound connections. Because this skill controls cameras, misuse could combine data exfiltration with unauthorized operational control paths.

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

Critical
Category
Data Flow
Content
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:
Confidence
83% confidence
Finding
Preset-management requests are sent to a URL derived from untrusted environment configuration. If that endpoint is replaced, the script will disclose signed request headers and device identifiers to an attacker-controlled service while performing state-changing operations. The risk is elevated here because the calls can alter device behavior, not just read data.

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

Critical
Category
Data Flow
Content
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:
Confidence
83% confidence
Finding
Tour-management operations also reuse the environment-derived URL without validation. An attacker who controls the runtime environment can redirect these authenticated, state-changing requests to an arbitrary host, potentially exposing control metadata and enabling network pivoting. Given the skill's purpose, unauthorized modification of patrol behavior has real operational impact.

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

Critical
Category
Data Flow
Content
body = {"Name": "Uart.PTZPreset"}
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:
Confidence
80% confidence
Finding
The preset-list query sends authenticated headers to a URL constructed from JF_ENDPOINT. While this path is read-oriented, an attacker-controlled endpoint could still harvest request metadata, signatures, and device tokens and use the script as an outbound connector. The impact is somewhat lower than state-changing calls but still security-relevant.

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

Critical
Category
Data Flow
Content
body = {"Name": "Uart.PTZTour"}
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    
    if result.get("code") != 2000:
Confidence
80% confidence
Finding
The tour-config query posts to a host derived from environment input with no trust boundary enforcement. This allows exfiltration of signed headers and device-related identifiers to arbitrary destinations if the runtime environment is tampered with. Even though this function is primarily informational, it still creates an unnecessary authenticated egress channel.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documentation clearly instructs use of environment variables containing sensitive credentials and device tokens, and describes outbound API access, but no explicit permission declaration is present. That creates a real trust and review gap: operators may invoke a skill with network and secret access without an upfront, machine-readable declaration of those capabilities.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The trigger phrases are broad and overlap with common natural language such as direction, zoom, preset, and patrol-related words. In a PTZ control skill, accidental invocation can directly move cameras, change surveillance coverage, or start automated patrol behavior, making misfires operationally and privacy-relevant rather than merely inconvenient.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This skill controls live camera orientation, zoom, presets, and tours, but the description lacks a clear warning that activating it can alter monitoring coverage and affect privacy or physical-site awareness. Users may not realize that a simple command can redirect a surveillance device away from intended areas or expose other areas, increasing the chance of unsafe or privacy-invasive use.

Static analysis

No suspicious patterns detected.