Back to skill

Security audit

jf-open-pro-video-masking

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent for controlling a JFTech camera privacy mask, but it has review-worthy risks around credential-bearing API calls and a misleading toggle command that can disable privacy masking.

Review before installing. Only use this with credentials and device tokens you are willing to grant camera privacy-mask control, keep JF_ENDPOINT set to one of the documented JFTech hosts, and avoid the toggle action because it does not actually determine state and can turn masking off. Prefer explicit enable or disable commands until the endpoint validation and toggle behavior are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/video_masking.py:25
Finding

Unvalidated API Endpoint Permits Device Token and Authentication Metadata Disclosure

Content
View full analysis

Vulnerability Details

File Location: scripts/video_masking.py, lines 25–66
Vulnerability Type: Unvalidated destination for sensitive network 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 a 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 set_video_masking(device_token: str, uuid: str, app_key: str,
                      app_secret: str, move_card: int,
                      enable: bool) -> Dict[str, Any]:
    """
    Set the video privacy-mask state.

    API: POST /gwp/v3/rtc/device/setconfig/{deviceToken}
    Name: OPPTZControl
    """
    url = f"{JF_BASE_URL}/rtc/device/setconfig/{device_token}"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    
    body = {
        "Name": "OPPTZControl",
        "General.OneKeyMaskVideo": [
            {
                "Enable": enable
            }
        ]
    }
    
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()

Technical Analysis

The JF_ENDPOINT environment variable is inserted directly into the request URL without hostname validation or an allowlist. The documentation identifies only four legitimate regional API hosts, but the implementation permits requests to an ...[truncated 2038 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the unrestricted endpoint setting with a region selector mapped internally to an immutable allowlist:
    • api-cn.jftechws.com
    • api-as.jftechws.com
    • api-eu.jftechws.com
    • api-na.jftechws.com
  2. If endpoint customization must remain available, parse it as a hostname and reject:
    • Unknown domains
    • URL schemes
    • User-information components
    • Ports
    • Paths and query strings
    • IP address literals
    • Encoded or ambiguous hostnames
  3. Disable HTTP redirects for authenticated requests, or validate every redirect destination against the same allowlist.
  4. Avoid placing bearer-like device tokens in URL paths where the upstream API permits an authorization header instead.
  5. Ensure tokens are narrowly scoped, short-lived, revocable, and restricted to the minimum required device operation.
  6. Add automated tests confirming that unsupported hosts and redirect destinations are rejected before any credentials are transmitted.

other

Warning
Location
scripts/video_masking.py:168
Finding

Toggle Action Always Disables Privacy Masking and May Unexpectedly Restore Recording

Content
View full analysis

Vulnerability Details

File Location: scripts/video_masking.py, lines 168–187
Vulnerability Type: Misleading privacy-control behavior
Risk Level: Medium

Vulnerable Code

python
def toggle_action(args: argparse.Namespace) -> int:
    """Execute the masking-state toggle operation."""
    try:
        print(f"Switching the masking state for device {args.device_sn}...")
        print()
        print("The current state cannot be queried; masking will be disabled.")
        print("If masking is enabled, this operation will disable it.")
        print("If masking is already disabled, this operation will have no effect.")
        print()
        
        # Attempt to disable masking
        result = set_video_masking(
            device_token=args.device_token,
            uuid=args.uuid,
            app_key=args.app_key,
            app_secret=args.app_secret,
            move_card=args.move_card,
            enable=False
        )

Technical Analysis

The Skill documentation describes toggle as switching the privacy-mask state in either direction. The implementation cannot determine the current state and unconditionally sends enable=False.

Therefore, toggle is not a toggle operation. When the camera is masked, invoking it restores the normal monitoring position, video preview, and recording. This is particularly security-sensitive because the feature is explicitly intended to protect privacy in homes and meeting rooms.

The console warning partially describes the implementation, but the command name, documented example, and success behavior still represent it as a state toggle. A privacy-protecting operation should fail closed when the current state cannot be established rather than defaulting to disabling protection.

Attack Path

  1. A user places the camera in privacy-mask mode.
  2. The user or an automation invokes --action toggle, relying on the documented b ...[truncated 902 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the toggle action until the current device state can be reliably queried.
  2. If a trustworthy state-query API becomes available, retrieve and validate the current state before sending the inverse value.
  3. If local state caching is used, protect it against corruption and concurrent updates, and do not treat stale or missing state as authorization to disable masking.
  4. Fail closed when the state is unknown: preserve masking and instruct the user to choose an explicit enable or disable action.
  5. Require explicit confirmation before any operation that disables privacy masking and restores preview or recording.
  6. Update the documentation and command output so they accurately describe behavior.
  7. Add tests verifying that a toggle operation never disables masking when the existing state cannot be established.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

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

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The request URL is built from the JF_ENDPOINT environment variable and used directly in requests.post while sending authentication headers including uuid, appKey, timeMillis, and signature. If an attacker can influence the environment, they can redirect traffic to an attacker-controlled host and capture signed requests or device-control operations, which is a real SSRF/exfiltration risk in an agent or automation environment.

Content

Scanner excerpt · scripts/video_masking.py (reported line 68)May include surrounding context.

python
]
    }
    
    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
98% confidence
Finding

The skill's high-level declaration is incomplete relative to its operational behavior: it performs enable/disable/toggle/status flows, uses platform and device credentials, and makes remote API calls while permissions are undeclared. This matters because users and automated systems may underestimate both the control surface over the device and the exposure of sensitive credentials during execution.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The skill's high-level declaration is incomplete relative to its operational behavior: it performs enable/disable/toggle/status flows, uses platform and device credentials, and makes remote API calls while permissions are undeclared. This matters because users and automated systems may underestimate both the control surface over the device and the exposure of sensitive credentials during execution.

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

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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documentation declares no tool scope or permissions even though the skill clearly requires environment variable access for secrets and network access to a remote API. This is dangerous because it hides the skill's actual trust boundary and can cause an agent or reviewer to approve execution without understanding that credentials will be consumed and outbound requests will be made.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

技能描述和文件头功能列表都表明该技能支持查询状态与切换状态,但 status_action 仅输出“当前 API 不支持直接查询”,get_masking_status 直接返回 None;toggle_action 也只是尝试关闭遮蔽。对外宣称的能力范围比代码真实能力更强,构成描述与行为不一致。

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/video_masking.py (reported line 68)May include surrounding context.

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

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

模块说明将 toggle 描述为“切换遮蔽状态”,函数注释也写成“执行切换遮蔽状态操作”,但实现中由于无法查询当前状态,固定调用 set_video_masking(..., enable=False)。这不是单纯信息不完整,而是文档/提示语宣称具备双向切换能力,代码实际上仅执行关闭遮蔽。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code file's top-level docstring and inline documentation are entirely in Chinese, and the file does not indicate that the skill is region-specific or that users can opt into this language. Under the policy, forcing a specific language without user choice can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

This code file contains user-facing natural-language descriptions entirely in Chinese, including the module docstring and CLI help text, with no indication that another language is available. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.

Content

No source excerpt is available for this finding.