Back to skill

Security audit

jf-garbage-inspection

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its camera-inspection purpose, but it needs Review because it can store or expose credentials and uses under-scoped network downloads around camera images.

Install only if you are comfortable giving the skill access to JF camera credentials and snapshots. Before production use, restrict the API endpoint to known JF hosts, avoid storing device passwords in `config/cameras.json`, do not put real secrets into cron messages or prompts, pin Python dependencies, and define where captured images are stored and when they are deleted.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/capture_and_download.py:30
Finding
Unrestricted API Endpoint Receives Sensitive Authentication Metadata<![CDATA[ ## Vulnerability Details **File Location**: `scripts/capture_and_download.py:30-54` and `scripts/capture_and_download.py:72-77` **Vulnerability Type**: Improper validation of a security-sensitive network destination **Risk Level**: High ### Vulnerable Code ```python # API base address 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 the 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 get_device_tokens(device_sns: List[str], uuid: str, app_key: str, app_secret: str, move_card: int) -> Dict[str, str]: """Retrieve device tokens and return an SN-to-token mapping.""" url = f"{JF_BASE_URL}/rtc/device/token" headers = get_headers(uuid, app_key, app_secret, move_card) body = {"sns": device_sns, "accessToken": ""} response = requests.post(url, headers=headers, json=body, timeout=30) ``` The same unvalidated base URL is also used by `device_capture()`: ```python url = f"{JF_BASE_URL}/rtc/device/capture/{device_token}" headers = get_headers(uuid, app_key, app_secret, move_card) ... response = requests.post(url, headers=headers, json=body, timeout=30) ``` ### Technical Analysis `JF_ENDPOINT` is accepted directly from the process environment and interpolated into an HTTPS URL without checking whether the hostname belongs to the documented JF service. Requests to this endpoint contain the user's UUID, application key, timestamp, request identifier, device serial numbers, and a signatur ...[truncated 1781 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the free-form endpoint with a region selector mapped to fixed hosts: ```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_BASE_URL = f"https://{ALLOWED_ENDPOINTS[region]}/gwp/v3" ``` 2. If custom endpoints are operationally necessary, parse them with `urllib.parse` and reject user information, ports, IP literals, non-HTTPS schemes, and hosts outside an explicit allowlist. 3. Do not follow redirects for authenticated API requests, or validate every redirect target against the same allowlist. 4. Ensure errors and diagnostics never log headers, signatures, secrets, or full request objects. 5. Rotate credentials if signed requests have already been sent to an untrusted endpoint. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/capture_and_download.py:89
Finding
Server-Side Request Forgery Through Unvalidated Capture Image URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/capture_and_download.py:89-101` and `scripts/capture_and_download.py:172-183` **Vulnerability Type**: Server-side request forgery and unrestricted external resource retrieval **Risk Level**: High ### Vulnerable Code ```python def download_image(image_url: str, output_path: str, max_retries: int = 1) -> bool: """Download an image locally and retry once after failure.""" for attempt in range(max_retries + 1): try: resp = requests.get(image_url, timeout=30) if resp.status_code == 200: os.makedirs(os.path.dirname(output_path), exist_ok=True) with open(output_path, 'wb') as f: f.write(resp.content) return True except Exception: if attempt < max_retries: continue return False ``` The URL is taken directly from the API response: ```python data = device_capture( device_token=token_map[sn], uuid=uuid, app_key=app_key, app_secret=app_secret, move_card=move_card, channel=channel ) image_url = data.get("image", "") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{sn}_{timestamp}.png" output_path = os.path.join(output_dir, filename) if download_image(image_url, output_path): success_count += 1 if json_output: results.append({"sn": sn, "name": name, "location": location, "success": True, "file": output_path, "url": image_url}) ``` ### Technical Analysis The script performs an HTTP GET against any URL returned in the capture API's `image` field. It does not validate: - The URL scheme. - The destination hostname. - The destination's resolved IP addresses. - Whether the destination is loopback, link-local, private, or reserved. - Redirect targets. - Whether the response is actually an image. - The maximum response size. The `requests` library follows redirects by default. Consequently, eve ...[truncated 1885 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https` and allowlist the approved image-storage hostnames used by the JF service. 2. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved addresses for both IPv4 and IPv6. 3. Disable redirects with `allow_redirects=False`, or validate every redirect target and resolved address before following it. 4. Revalidate the connected peer to reduce DNS-rebinding risk. 5. Apply a strict response-size limit while streaming the body. 6. Verify `Content-Type` against expected image types and decode the image before treating the download as successful. 7. Avoid including signed image URLs in JSON output unless required; redact query tokens if such URLs contain credentials. 8. Prefer receiving image bytes through an authenticated, fixed-origin API rather than dereferencing arbitrary response URLs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:84
Finding
Unnecessary Plaintext Storage of Device Passwords<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:84-104` and `config/cameras.json:2-8` **Vulnerability Type**: Plaintext sensitive-data storage and excessive credential collection **Risk Level**: Medium ### Vulnerable Configuration Guidance ```json { "cameras": [ { "sn": "device serial number", "name": "Building A first-floor garbage bin", "password": "device password", "location": "Building A first-floor lobby" } ], "settings": { "overflow_threshold": "moderate", "default_channel": 0 } } ``` The documentation defines the field as follows: ```text - `sn`: JF device serial number - `name`: User-defined display name - `password`: Device password used to obtain a token automatically - `location`: Location description ``` The distributed configuration also reserves a plaintext password field: ```json { "cameras": [ { "sn": "", "name": "", "password": "", "location": "办公室" } ] } ``` ### Technical Analysis The Skill documentation directs users to persist camera device passwords in `config/cameras.json`. This is a regular plaintext file and no file-permission enforcement, encryption, secret-store integration, or redaction mechanism is provided. The actual implementation does not use the password when requesting device tokens. `inspect_devices()` consumes serial numbers, names, and locations, while authentication uses the application credentials from environment variables. The password field is therefore unnecessary for the implemented functionality and violates least-privilege and data-minimization principles. The bundled password value is empty, so the audited artifact does not itself expose a live device password. The vulnerability arises when users follow the documented configuration format. ### Attack Path 1. A user follows the Skill documentation and enters a camera password in `config/cameras.json`. 2. The password remains in plaintext in the Skill direc ...[truncated 725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `password` property from the documented schema, the bundled configuration, and camera-management instructions. 2. Do not request or retain device passwords when application credentials are sufficient. 3. If a future API genuinely requires the password, retrieve it at runtime from an operating-system keychain or managed secret store. 4. Restrict configuration-file permissions to the service account and exclude local configuration and capture directories from source control and general backups. 5. Add schema validation that rejects unexpected sensitive fields to prevent accidental persistence. 6. Provide migration guidance that removes existing password fields and recommends rotating any password previously committed or shared. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:318
Finding
Scheduled-Task Workflow Encourages Persistent Plaintext API Secrets<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:318-330` **Vulnerability Type**: Persistent secret exposure in scheduled-task definitions **Risk Level**: High ### Vulnerable Instructions ```text 2. Use `qoder_cron` to create a scheduled task with a message such as: Execute the following garbage-overflow inspection task: Skill directory: {skill_dir} Data directory: {data_dir} Step 1 — Capture Use the following command to capture images from all inspection cameras: Set PYTHONIOENCODING=utf-8 python {skill_dir}/scripts/capture_and_download.py --action inspect-batch --config {skill_dir}/config/cameras.json --output-dir {data_dir}/captures/{today YYYYMMDD}/ --json Environment variables: JF_UUID=xxx, JF_APP_KEY=xxx, JF_APP_SECRET=xxx, JF_MOVE_CARD=xxx ``` Related environment-check guidance also recommends checking variables through shell output: ```text Check whether `JF_UUID`, `JF_APP_KEY`, and `JF_APP_SECRET` are set (using `echo %JF_UUID%` or a Bash command). ``` ### Technical Analysis The scheduled-task example places authentication values directly in the task message. Scheduled-task definitions are generally persistent and may be accessible through task listings, administrative interfaces, execution histories, logs, notifications, or backups. If the placeholders are replaced with real values as implied by the workflow, `JF_APP_SECRET` becomes long-lived plaintext outside a dedicated secret-management boundary. This is not necessary: the task can inherit protected environment variables or resolve secret references at execution time. The related recommendation to use `echo` can also print values into tool output or logs. Presence checks should not reveal secret contents. Creating a legitimate user-requested inspection schedule is part of the declared functionality and is not itself malicious persistence. The vulnerability is the unnecessary embedding and display of credentials in that persistent task. ### Attack Path 1. A user requests ...[truncated 993 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never interpolate secret values into cron messages, command lines, prompts, task descriptions, or logs. 2. Store credentials in a managed secret store or protected runtime environment and reference only secret identifiers from the task. 3. Configure the scheduler so the execution process receives secrets through a protected injection mechanism. 4. Replace output-producing checks with non-disclosing presence tests, for example: ```bash test -n "${JF_UUID:-}" && test -n "${JF_APP_KEY:-}" && test -n "${JF_APP_SECRET:-}" ``` 5. Restrict access to scheduled-task definitions and execution history. 6. Redact secrets from errors and tool traces. 7. Rotate credentials and recreate task definitions if real secrets were previously embedded. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:76
Finding
Unpinned Runtime Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:76-79` **Vulnerability Type**: Unpinned third-party dependency and non-reproducible installation **Risk Level**: Medium ### Vulnerable Instructions ```bash pip install requests ``` ### Technical Analysis The installation instruction retrieves the latest package version resolved by the user's configured Python package index. It provides no version constraint, lock file, artifact hash, trusted-index requirement, or isolated-environment guidance. The dependency name `requests` is legitimate and there is no evidence that the project intentionally references a malicious or typosquatted package. The risk is that future versions, compromised package-index infrastructure, unsafe mirror configuration, or altered transitive dependencies can change the code installed after the Skill has been reviewed. Because Python packages can execute build and installation logic, dependency compromise may lead to code execution under the privileges of the user running `pip`. ### Attack Path 1. A user follows the documented installation command. 2. `pip` queries the configured package index or mirror. 3. The resolver selects mutable package and transitive-dependency versions. 4. A compromised index, mirror, account, or dependency supplies a malicious artifact. 5. Installation or later import executes attacker-controlled Python code with the installing user's privileges. ### Impact Assessment Successful supply-chain exploitation can execute arbitrary code as the account performing installation or running the Skill. This can expose API credentials, camera images, local files accessible to that account, and network resources reachable from the host. There is no evidence of an active malicious dependency in the audited project. The finding concerns the absence of controls needed for reproducible and verifiable dependency installation. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin an audited version of `requests` and its transitive dependencies in a lock file. 2. Use hash-verified installation, for example a generated requirements file containing `--hash` entries and: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Install dependencies inside a dedicated virtual environment using a supported Python version. 4. Configure an approved HTTPS package index and disable untrusted extra indexes. 5. Periodically scan and update the lock file through a controlled review process. 6. Document the expected package versions rather than instructing users to install the latest release dynamically. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (21)

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

Critical
Category
Data Flow
Content
url = f"{JF_BASE_URL}/rtc/device/token"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    body = {"sns": device_sns, "accessToken": ""}
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    if result.get("code") != 2000:
        raise RuntimeError(f"获取设备 Token 失败:{result.get('msg', '未知错误')}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
url = f"{JF_BASE_URL}/rtc/device/token"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    body = {"sns": device_sns, "accessToken": ""}
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    if result.get("code") != 2000:
        raise RuntimeError(f"获取设备 Token 失败:{result.get('msg', '未知错误')}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
该代码块的核心功能是与杰峰平台 API 交互完成摄像头抓图及本地下载,这与描述中的“巡检”前置步骤一致,但并不等同于完整的垃圾溢出巡检能力。描述强调的关键能力是基于图片进行垃圾桶溢出分析并产出结构化报告;而代码只返回文件路径、URL、成功失败等元数据,没有任何图像识别、AI 判断、阈值分析或报告生成逻辑。另外,虽然声明提到支持单设备/批量巡检,代码确实支持 inspect-single 和 inspect-batch,但“定时任务”支持在该代码中并未实现。综上,描述明显高于代码实际行为,存在实质性能力不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个面向视频监控图像的垃圾桶溢出巡检技能,预期应看到设备抓图、图片输入处理、视觉分析、结果判定与报告输出等逻辑。但实际代码仅是一个独立的 OpenAPI 签名工具,主要用于生成时间戳和请求签名,属于底层认证辅助模块。虽然它可能是调用杰峰接口的配套实现细节,但单就此代码块而言,其行为与声明的主要用途明显不一致,且缺失声明中的核心功能,因此应判定为描述与代码行为不匹配。

Missing User Warnings

High
Confidence
99% confidence
Finding
The scheduled task example embeds secret environment variable values directly into the cron message content. Putting credentials into task definitions or message bodies can leak them through logs, scheduler metadata, prompts, debugging output, or downstream messaging and constitutes a direct secret-exposure path.

Ssd 3

High
Confidence
99% confidence
Finding
This instruction explicitly tells the agent to propagate environment-variable secrets as part of a natural-language scheduled workflow that also pushes results via IM. That creates a high-probability leakage channel because secrets may be exposed in scheduler storage, model context, or messaging flows far outside the minimal trusted boundary.

Ae1

High
Category
analysis-evasion
Content
| `SKILL.md` | 技能文档 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares capabilities that require environment access, file reads, and network operations, but it does not explicitly scope or constrain those tools. This weakens least-privilege boundaries and makes it harder for reviewers or the runtime to prevent the skill from accessing more data or performing more actions than users expect.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger set contains broad everyday phrases such as requests about checking garbage or adding cameras, which can cause unintended invocation in unrelated conversations. Overbroad activation increases the chance that the agent performs file, network, or scheduling actions without sufficiently specific user intent.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill says captured camera images are downloaded locally and later displayed, but it does not provide a clear privacy notice or consent boundary. Camera snapshots can contain sensitive visual data about people, locations, or property, so silent local download and presentation increases privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to store device passwords in a local JSON config file but does not provide a clear warning or safer handling guidance. Plaintext credential storage materially increases the risk of credential theft through local compromise, accidental sharing, backups, or other skills reading the file.

Vague Triggers

Medium
Confidence
88% confidence
Finding
Allowing the agent to decide on its own that the user is a first-time user creates an unclear activation boundary. That can lead to unsolicited environment checks, file reads, or setup actions without an explicit user request, which is especially risky in a skill that touches secrets and camera data.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill extends from image inspection into IM enumeration and scheduled workflow creation, which broadens its operational reach beyond the core stated task. This creates unnecessary access to messaging metadata and automation surfaces, increasing the chance of unauthorized notifications, spam, or lateral data exposure if misused.

External Transmission

Medium
Category
Data Exfiltration
Content
url = f"{JF_BASE_URL}/rtc/device/token"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    body = {"sns": device_sns, "accessToken": ""}
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    if result.get("code") != 2000:
        raise RuntimeError(f"获取设备 Token 失败:{result.get('msg', '未知错误')}")
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
url = f"{JF_BASE_URL}/rtc/device/token"
    headers = get_headers(uuid, app_key, app_secret, move_card)
    body = {"sns": device_sns, "accessToken": ""}
    response = requests.post(url, headers=headers, json=body, timeout=30)
    result = response.json()
    if result.get("code") != 2000:
        raise RuntimeError(f"获取设备 Token 失败:{result.get('msg', '未知错误')}")
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code fetches image content from a remote URL and writes it to disk, which can affect user privacy and local data storage. Although the script logs success/failure, it does not provide any explicit warning or disclosure that camera snapshots will be captured from devices and saved locally.

Tainted flow: 'image_url' from requests.post (line 172, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
"""下载图片到本地,失败重试一次"""
    for attempt in range(max_retries + 1):
        try:
            resp = requests.get(image_url, timeout=30)
            if resp.status_code == 200:
                os.makedirs(os.path.dirname(output_path), exist_ok=True)
                with open(output_path, 'wb') as f:
Confidence
92% confidence
Finding
The script downloads a URL returned by a remote API without validating scheme, host, or address range. If the upstream service, endpoint configuration, or response path is compromised, this can be used for SSRF-style outbound requests to internal services or unexpected large/unsafe downloads.

Context-Inappropriate Capability

Low
Confidence
88% confidence
Finding
The onboarding flow instructs the agent to inspect environment variables via shell commands, introducing command-execution behavior where a safer native environment check should be used. Even if the immediate purpose is simple validation, normalizing shell access for secrets handling increases the risk of accidental disclosure, logging, or injection in adjacent workflows.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The value "办公室" sets a Chinese-language location label in the configuration, which can represent a language/locale policy issue when the skill does not offer any user choice or document that it is intended for a Chinese-specific deployment. The file contains no indication that this locale constraint is optional or justified.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
Natural-language text in the docstring and CLI help messages is presented only in Chinese, with no indication that language selection is optional or that the tool is intentionally restricted to a Chinese-speaking context. This can violate language/locale policy where user-facing skills should not force a single language without opt-in.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This code file presents its natural-language interface entirely in Chinese, including the module docstring and later user-facing note, with no indication that language choice is optional. Under the policy rule, forcing a specific language without user opt-in is a natural-language policy concern unless the locale restriction is explicitly justified.

Static analysis

Detected: suspicious.env_credential_access

Python code POSTs credential environment variables to an environment-controlled URL.

Critical
Code
suspicious.env_credential_access
Location
scripts/capture_and_download.py:54