Back to skill

Security audit

jf-open-pro-feed-control

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real pet-feeder control tool, but it can send signed credentials and device commands to an arbitrary endpoint selected by environment variable.

Review before installing. Use this only with trusted JFTech credentials and a trusted runtime environment, keep JF_ENDPOINT set only to an official vendor host, and require explicit user confirmation before any feeding, schedule-changing, or pet-detection setting action. Rotate credentials if they may have been used with an untrusted endpoint.

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/feed_control.py:29
Finding

Unrestricted API Endpoint Enables Disclosure of Authentication Material and Device Tokens

Content
View full analysis

Vulnerability Details

File Location: scripts/feed_control.py, lines 29–30, 33–44, 69–79, and 100–105
Vulnerability Type: User-controlled network destination for sensitive API requests
Risk Level: High

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]:
    """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()
    }
python
base_url = f"https://{endpoint}/gwp/v3"
url = f"{base_url}/rtc/device/token"
headers = get_headers(uuid, app_key, app_secret, move_card)

body = {
    "sns": sns
}
if access_token:
    body["accessToken"] = access_token

response = requests.post(url, headers=headers, json=body, timeout=30)
python
url = f"{JF_BASE_URL}/rtc/device/getability/{device_token}"
headers = get_headers(uuid, app_key, app_secret, move_card)

body = {"Name": "FeederAbility"}

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

The same unrestricted base URL is used by the other feeder-control, schedule, and pet-detection requests.

Technical Analysis

The JF_ENDPOINT environment variable is directly interpolated into request URLs without validation against the four vendor hosts documented in SKILL.md. Consequently, a process capable of modifying the Skill's environment or launch configuration can redirect all API traffic to an arbitrary HTTPS server.

These requests include sensitive authentication a ...[truncated 2724 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace the free-form endpoint with a region selector and map each allowed region to a fixed hostname:

    python
    ALLOWED_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 ALLOWED_ENDPOINTS:
        raise ValueError("Unsupported JF region")
    
    JF_ENDPOINT = ALLOWED_ENDPOINTS[region]
    JF_BASE_URL = f"https://{JF_ENDPOINT}/gwp/v3"
    
  2. If hostname configuration must remain available, require an exact match against the four documented hostnames. Reject URL schemes, paths, ports, user-information components, wildcard subdomains, IP literals, and trailing-dot variants.

  3. Disable redirects for requests carrying sensitive credentials:

    python
    response = requests.post(
        url,
        headers=headers,
        json=body,
        timeout=30,
        allow_redirects=False,
    )
    
  4. Centralize request construction in one validated API client so every action uses the same destination policy and TLS settings.

  5. Avoid placing device tokens in URL paths if the vendor API supports an authorization header. URL paths are more likely to be retained in proxy, server, monitoring, and error logs.

  6. Keep tokens short-lived, rotate any credentials suspected of having been sent to an untrusted endpoint, and ensure server-side replay protection validates timestamps, request identifiers, intended audience, and request content.

  7. Document the exact external hosts required by the Skill and apply runtime egress controls that permit only those hosts over HTTPS.

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

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

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The request destination is derived from the JF_ENDPOINT environment variable and then used to send signed headers and, in some cases, access tokens to that host. In a skill context, environment variables are deployment-controlled rather than user-controlled, but if an attacker or misconfigured runtime can override JF_ENDPOINT, the skill will exfiltrate credentials and device-control traffic to an arbitrary server, enabling SSRF-style abuse and credential leakage.

Content

Scanner excerpt · scripts/feed_control.py (reported line 79)May include surrounding context.

python
if access_token:
        body["accessToken"] = access_token

    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 230, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

This POST uses a URL built from JF_BASE_URL, which in turn depends on the JF_ENDPOINT environment variable. Because the request includes signed authentication headers, a compromised or incorrectly set endpoint can redirect authenticated device capability queries to an attacker-controlled server and disclose sensitive metadata and signing material context.

Content

Scanner excerpt · scripts/feed_control.py (reported line 105)May include surrounding context.

python
body = {"Name": "FeederAbility"}

    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 230, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
98% confidence
Finding

The feed command is sent to a URL whose host is influenced by an environment variable, while carrying authentication headers and device identifiers. If that host is redirected, an attacker can capture credentials/signatures and potentially replay or proxy control actions, which is especially dangerous because this skill performs real-world device operations affecting a pet feeder.

Content

Scanner excerpt · scripts/feed_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 230, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

The schedule retrieval call sends authenticated device information to a URL derived from an environment variable. In an agent skill, that enables exfiltration of device metadata and account-scoped request signatures if deployment configuration is tampered with, creating an avoidable trust-boundary violation.

Content

Scanner excerpt · scripts/feed_control.py (reported line 160)May include surrounding context.

python
"props": ["feedPlan"]
    }

    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 230, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
98% confidence
Finding

This schedule-setting request transmits authenticated commands and user-supplied schedule data to a host selected from environment configuration. If the endpoint is malicious, it can collect control payloads and auth material or induce unauthorized schedule manipulation through request interception/proxying; the physical-device context raises the severity beyond a normal API client issue.

Content

Scanner excerpt · scripts/feed_control.py (reported line 191)May include surrounding context.

python
if method:
        body["props"]["method"] = method

    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 230, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The pet-detection status request posts signed headers to a URL built from an environment-controlled base URL. Although the payload is small, redirecting authenticated device queries to an attacker-controlled endpoint can leak account/device context and facilitate broader abuse of the API integration.

Content

Scanner excerpt · scripts/feed_control.py (reported line 213)May include surrounding context.

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

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

    if result.get("code") != 2000:

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

Critical
Category
Data Flow
Confidence
98% confidence
Finding

This setting-changing API call sends authenticated device-control traffic to a host influenced by JF_ENDPOINT. Because it changes device behavior, a malicious endpoint can observe or tamper with commands, and the skill’s IoT nature makes misrouting especially dangerous due to possible unintended physical effects or loss of user trust.

Content

Scanner excerpt · scripts/feed_control.py (reported line 235)May include surrounding context.

python
body = {"Switch": switch}

    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 documented purpose claims full feeder-control functionality, while the available implementation reportedly only provides signing/timestamp helper behavior and does not actually implement feeding, schedule management, or pet-detection control. This mismatch is dangerous because reviewers and users may trust the skill to perform one narrowly understood function while it actually exposes different capabilities or hidden execution paths, undermining informed consent and security review. In security terms, deceptive or inaccurate documentation masks real behavior and can conceal misuse of secrets or future unauthorized network actions.

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

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

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill documentation defines environment-variable use, local script execution, and outbound API access, but it does not declare any explicit tool scope such as allowed-tools or permissions. That omission weakens reviewability and least-privilege enforcement, making it easier for an agent runtime to grant broader capabilities than the skill actually needs. In a skill that can read secrets from env and issue remote commands to a physical device, undeclared capability boundaries increase operational and security risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation describes commands that can immediately dispense food or alter automated feeding schedules for a physical device, but it does not prominently warn that these actions have real-world effects. Without explicit safety messaging, confirmation requirements, or misuse constraints, an agent or user could trigger unintended feeding, repeated dispensing, or schedule changes affecting animal care. The physical-device context makes this more sensitive than ordinary API operations because mistakes can directly affect pets and household devices.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains natural-language descriptions, help text, and status/error messages entirely in Chinese, starting with the module docstring and continuing throughout the CLI. Under the stated policy, forcing a specific language without user opt-in is a locale policy violation unless the constraint is clearly documented and justified, which is not present here.

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

python
if access_token:
        body["accessToken"] = access_token

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

python
if access_token:
        body["accessToken"] = access_token

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

python
if access_token:
        body["accessToken"] = access_token

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

python
if access_token:
        body["accessToken"] = access_token

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

python
if access_token:
        body["accessToken"] = access_token

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

python
if access_token:
        body["accessToken"] = access_token

    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
94% confidence
Finding

This Python file contains its module docstrings and runtime user-facing message entirely in Chinese, including the note printed to users at execution. The file does not provide an English alternative, user opt-in, or any justification that the skill is intended only for a Chinese-speaking or region-specific environment, which creates a language/locale policy concern.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The function get_feed_schedule is documented as '获取定时喂食计划' but its docstring names the API as POST /gwp/v3/rtc/device/iotPropSet/{deviceToken}, where iotPropSet conventionally denotes a property-setting endpoint rather than a read/query endpoint. This is an active documentation contradiction because the surrounding code and caller behavior treat the function as a read operation.

Content

No source excerpt is available for this finding.