Back to skill

Security audit

jf-open-pro-local-record

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it says, but it can send device tokens and passwords to an unrestricted API hostname chosen from the environment.

Review before installing. Only use JF_ENDPOINT with the documented JFTech host for your region, keep app secrets, device tokens, and device passwords in a protected secret store, and rotate them if the endpoint or runtime environment may have been modified. Be aware that the skill can access private recording metadata, produce playback/download/image URLs, and switch the device recording stream setting.

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/local_record.py:31
Finding

Arbitrary API Endpoint Can Receive Device Access Credentials

Content
View full analysis

Vulnerability Details

File Location: scripts/local_record.py:31-32, scripts/local_record.py:38-47, and scripts/local_record.py:129-145
Vulnerability Type: Unvalidated network destination for sensitive credentials
Risk Level: Medium

Vulnerable Code

python
# API base address, configurable 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]:
    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()
    }


url = f"{JF_BASE_URL}/rtc/device/playbackUrl/{device_token}"
headers = get_headers(uuid, app_key, app_secret, move_card)

body = {
    "channel": channel,
    "streamType": stream_type,
    "protocol": protocol,
    "startTime": start_time,
    "endTime": end_time,
    "fileName": file_name,
    "username": username,
    "password": password,
    "download": download,
    "playPrioritize": play_prioritize
}

response = requests.post(
    url, headers=headers, json=body, timeout=30
)

Technical Analysis

The destination hostname is taken directly from the JF_ENDPOINT environment variable and interpolated into the HTTPS base URL without validation or restriction to documented JFTech domains.

All API operations place the device access token in the URL path. Requests also disclose the user UUID, application key, timestamp, and a signature derived from the application secret. Playback and download requests additionally transmit the device u ...[truncated 2074 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the arbitrary hostname setting with a fixed region selector and map each accepted region to a trusted endpoint:

    python
    TRUSTED_ENDPOINTS = {
        "CN": "api-cn.jftechws.com",
        "AS": "api-as.jftechws.com",
        "EU": "api-eu.jftechws.com",
        "NA": "api-na.jftechws.com",
    }
    
    region = os.getenv("JF_REGION", "CN").upper()
    if region not in TRUSTED_ENDPOINTS:
        raise ValueError("Unsupported JFTech API region")
    
    JF_BASE_URL = (
        f"https://{TRUSTED_ENDPOINTS[region]}/gwp/v3"
    )
    
  2. If direct endpoint configuration must remain available, parse it strictly and require an exact hostname match against the documented allowlist. Reject user information, ports, paths, query strings, fragments, IP addresses, and deceptive suffixes.

  3. Do not use substring or suffix-only checks such as endswith("jftechws.com"); require exact equality with an approved hostname.

  4. Avoid supplying secrets through command-line arguments because they may be exposed in shell history or process listings. Prefer protected environment injection or an operating-system secret manager.

  5. Redact device tokens, signatures, passwords, and generated media URLs from logs and exception telemetry.

  6. If supported by the upstream API, move access tokens out of URL paths and into authorization headers to reduce exposure through URL logging.

  7. Rotate the device token and password after any suspected endpoint manipulation or credential disclosure.

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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/local_record.py (reported line 73)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 197, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/local_record.py (reported line 109)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 197, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/local_record.py (reported line 176)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 197, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/local_record.py (reported line 204)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 197, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This request sends device username and password in the JSON body to an endpoint whose hostname is overridable via JF_ENDPOINT. If that environment variable is misconfigured or attacker-controlled in the execution environment, device credentials can be exfiltrated to an unintended remote server, making this more dangerous than the other endpoint calls.

Content

Scanner excerpt · scripts/local_record.py (reported line 145)May include surrounding context.

python
"playPrioritize": play_prioritize
    }
    
    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

A documented behavior mismatch is security-relevant because users and reviewers may approve the skill for benign local-record operations while the implementation performs additional signing/crypto logic and omits key declared functions. This breaks transparency, undermines trust review, and can conceal hidden authentication or request-generation behavior that materially changes the risk profile.

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

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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documentation describes use of environment variables and outbound API access but does not declare any explicit tool scope such as allowed-tools or permissions. In an agent setting, missing scope declarations weakens least-privilege boundaries and can allow broader-than-expected access to secrets in the environment and network resources during execution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

该技能在“触发词”中列出“本地录像”“录像回放”“下载录像”等通用表达,但没有说明触发上下文、排除条件或负面示例。这些短语可出现在普通设备使用讨论中,容易与日常请求重叠,导致技能在非预期场景被调用。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill requires highly sensitive material including app secrets, device tokens, usernames, and device passwords, yet the documentation lacks explicit warnings about credential handling, storage, masking, rotation, and privacy implications of accessing surveillance recordings and alarm images. In this context, compromise of these values could expose device access, private video history, and downloadable media URLs.

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/local_record.py (reported line 73)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/local_record.py (reported line 109)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/local_record.py (reported line 176)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/local_record.py (reported line 204)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
91% confidence
Finding

The playback/download URL request includes device username and password in the outbound request body, yet the user-facing flow does not clearly warn that device credentials are being transmitted to a cloud API. In a skill context handling local recordings, this creates avoidable privacy and credential-exposure risk, especially if logs, proxies, or misconfigured endpoints capture the request.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

This external transmission is materially sensitive because the request body includes device credentials for playback/download setup. In the context of a local-recording skill, sending raw device credentials off-device raises the impact of interception, logging, or endpoint misconfiguration and can enable unauthorized device/media access.

Content

Scanner excerpt · scripts/local_record.py (reported line 145)May include surrounding context.

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

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

描述字段与全文文档均以中文呈现,且未说明该技能仅面向特定中文用户群体,也未提供语言选择或多语言说明。按照语言/区域政策,若强制单一语言而没有用户选择或明确合理性说明,属于自然语言层面的合规风险。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code file presents its natural-language interface entirely in Chinese, including the module docstring, function documentation, and the user-facing print message at runtime. Under the policy, forcing a specific language without opt-in or justification is a natural-language policy violation unless the locale restriction is explicitly documented as required.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

L132-L133 将 generate_signature 的 move_card 参数声明为 int,且 change() 也在 L80 将其定义为 int,并在 L99 中按取模运算使用。 但测试代码在 L174 将 move_card 设为字符串 "your-movecard-here",随后在 L176 直接传入该函数,这与文档化的参数类型和实际运算要求相冲突。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring and all user-facing CLI output are written only in Chinese, which imposes a language choice on users without opt-in or documented locale scope. The policy requires flagging language or locale constraints when the skill forces a specific language without user choice or clear justification.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The manifest describes a local-record skill for querying calendars, playback lists, downloads, alarm images, and stream switching. While network access to the vendor API is expected, directly sourcing multiple sensitive credentials and device passwords from process environment variables adds a credential-access capability that is not part of the stated functional scope.

Content

No source excerpt is available for this finding.