T09 · Insecure Skill Coding Practices
- 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: HighVulnerable 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_ENDPOINTenvironment variable is directly interpolated into request URLs without validation against the four vendor hosts documented inSKILL.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
-
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" -
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.
-
Disable redirects for requests carrying sensitive credentials:
python response = requests.post( url, headers=headers, json=body, timeout=30, allow_redirects=False, ) -
Centralize request construction in one validated API client so every action uses the same destination policy and TLS settings.
-
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.
-
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.
-
Document the exact external hosts required by the Skill and apply runtime egress controls that permit only those hosts over HTTPS.
-
