Back to skill

Security audit

Sick Poultry/Swine Behavior Detection | 病鸡/病猪行为识别

Security checks for vulnerabilities and agentic risk

Overview

This livestock-video skill needs review because its export-only option still runs analysis and its stored account credentials lack adequate safeguards.

Selected footage leaves your device for LifeEmergence's service, and account tokens persist locally. Do not rely on --export-env-only to prevent uploads. Before using sensitive footage, request fixes for export-only handling, credential destination checks, token protection, and debug logging; confirm the provider's retention terms and charges.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
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
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (23)

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The visible implementation is generic skill scaffolding with an incomplete chat wrapper, materially different from the declared livestock-health video analyzer. No subprocess is actually executed, and there is no evidence of video processing or health-risk outputs in this chunk. Imported base/API implementations are not supplied, so their behavior cannot be assessed; the mismatch assessment is limited to the code shown.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The supplied chunk primarily manages a user database rather than detecting animal behavior. It contains no video ingestion, behavioral inference, risk classification, or early-warning logic. Database storage can be a legitimate supporting implementation detail, but this chunk specifically manages human user identities and token fields without any demonstrated connection to the declared purpose. This assessment is limited to the supplied code; it does not establish whether other files implement the advertised analysis, and no token exfiltration is shown.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The static finding reports credential-bearing requests to caller-supplied endpoints, and the documentation exposes an unrestricted-looking --api-url override. If stored credentials are attached to an attacker-selected endpoint, they can be disclosed along with farm media or report data; this is the substantive security issue, rather than account or balance functionality merely existing. Confirmation requires inspecting the unavailable request-building code, including credential selection and redirect behavior.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: "smyx-sick-poultry-behavior-detect-analysis"
description: "Detects morbid behavioral cues in poultry and pigs from continuous barn videos — such as difficulty standing, ruffled feathers/piloerection, isolation, drowsiness and appetite loss — and outputs behavior type with risk level up to 2-3 days ahead of visible clinical signs. | 识别站立困难、羽毛蓬松、离群、嗜睡等病态行为,比人工观察提前2-3天。"
version: "1.0.16"
license: "MIT-0"
---

# 🐔 Sick Poultry / Pig Behavior Detection | 病鸡/病猪行为识别

> 识别站立困难、羽毛蓬松、离群、嗜睡等病态行为,比人工观察提前

The HTTP helper forwards service credentials to arbitrary absolute URLs.

High
Category
Excessive Agency
Confidence
99% confidence
Finding

The helper accepts arbitrary absolute HTTP or HTTPS URLs and attaches available service tokens and API keys without checking the destination. If an attacker can influence a caller's URL, a request to an attacker-controlled endpoint exposes those credentials; HTTP destinations additionally expose them to network interception. A video-analysis skill has no inherent need to send service authentication credentials to arbitrary media or third-party endpoints.

Content

No source excerpt is available for this finding.

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

The automatic trigger includes the generic phrase “历史报告” (“historical reports”) without a poultry/pig-health scope restriction.

Medium
Category
Trigger Abuse
Confidence
95% confidence
Finding

The generic historical-reports trigger is paired with mandatory automatic cloud querying, allowing unrelated report requests to initiate this skill's authenticated workflow. This creates a scope-confusion risk involving unintended network activity and potentially displaying farm reports in an unrelated conversation, although actual cross-user exposure is not demonstrated.

Content

No source excerpt is available for this finding.

The --export-env-only option promises not to run analysis, but the code ignores it and executes analysis.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

The --export-env-only flag explicitly promises not to execute analysis, but its parsed value is never checked; when an input or URL is supplied, execution still reaches skill.get_output_analysis. This defeats the user's execution boundary and could cause unintended processing or disclosure of sensitive barn footage if the downstream implementation uploads inputs, although that implementation is not shown.

Content

No source excerpt is available for this finding.

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

Scanner excerpt · skills/smyx_analysis/scripts/skill.py (reported line 28)May include surrounding context.

python
result_json = JsonUtil.parse(result_json_pure_text, result_json_pure_text)

        result_json_common_ai_response = result_json.get("commonAiResponse") if isinstance(result_json,
                                                                                           dict) else result_json
        if result_json_common_ai_response:
            result_json = result_json_common_ai_response

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

Scanner excerpt · skills/smyx_analysis/scripts/skill.py (reported line 33)May include surrounding context.

python
result_json = result_json_common_ai_response

        result_json_health_ai_response = result_json.get("healthAiResponse") if isinstance(result_json,
                                                                                           dict) else result_json
        if result_json_health_ai_response:
            result_json = result_json_health_ai_response

Local video contents are submitted to an analysis API without disclosure of the upload.

Medium
Category
Output Handling
Confidence
88% confidence
Finding

The code reads the complete local video and passes it to the analysis API without a visible upload disclosure or consent check. Barn footage can reveal workers, facility layouts, and commercially sensitive operations, so undisclosed remote processing creates a privacy risk. Uploading video is consistent with the skill's purpose rather than evidence of malicious exfiltration, but the API destination and any consent controls elsewhere are not available for verification.

Content

No source excerpt is available for this finding.

The documented export-only mode still executes video analysis.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

The --export-env-only flag explicitly promises not to execute analysis, but args.export_env_only is never checked, so supplying the flag together with --input or --url still calls skill.get_output_analysis. This violates an explicit execution boundary and could cause unintended processing, service charges, or disclosure of private barn footage if the downstream implementation uploads it; the actual transfer behavior is not visible in this file.

Content

No source excerpt is available for this finding.

The skill includes persistent user-profile and credential management unrelated to livestock behavior detection (L448–L473).

Medium
Category
Excessive Agency
Confidence
89% confidence
Finding

The User model persists token and open_token as ordinary string columns in a local SQLite database, with no encryption or credential-specific protection shown. Someone able to read the database or its backups could recover usable credentials if these fields contain active bearer tokens; the shared-library context makes user management plausible and is not itself evidence of malicious scope expansion.

Content

No source excerpt is available for this finding.

FileUtil.open silently truncates existing files without disclosing its destructive behavior.

Medium
Category
Output Handling
Confidence
97% confidence
Finding

The generically named open helper always uses mode 'w', which immediately erases an existing file's contents. No comment, docstring, confirmation, or operation log explains this behavior; the exception handler only handles failures after the attempt.

Content

No source excerpt is available for this finding.

Automatic remote login or registration transmits the current identity without disclosure.

Medium
Category
Output Handling
Confidence
91% confidence
Finding

The HTTP helper posts the current identity as both openId and mobile to a separate login endpoint with registration enabled. This auxiliary request has no explanatory comment, docstring, confirmation, or operation log, and automatic remote account registration is an additional side effect beyond issuing the caller's requested HTTP operation.

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 · skills/smyx_common/scripts/util.py (reported line 561)May include surrounding context.

python
"source": ConstantEnum.DEFAULT__SKILL_HUB_NAME
            }
            try:
                _response = requests.post(_url, json=_data)
                if _response.status_code == 200:
                    _response_json = _response.json()
                    if _response_json and _response_json.get("success"):

Configuration loading silently creates directories and writes missing configuration files, including during module import.

Low
Category
Output Handling
Confidence
93% confidence
Finding

YamlUtil.load creates parent directories and writes a YAML file when the requested path does not exist, without a comment, docstring, prompt, or user-facing log explaining this side effect. BaseEnum.init_subclass invokes this loader during class creation (L053 and L058), so importing the module can modify the filesystem without an explicit save request.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · skills/smyx_common/scripts/dao.py (reported line 243)May include surrounding context.

python
username = getattr(model, "username", None)
            if username:
                column_names = self.__model__.__table__.columns.keys()
                update_data = {key: getattr(model, key) for key in column_names if key != "username"}
                return self.update_by_username(username, **update_data)

            return None

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · skills/smyx_common/scripts/dao.py (reported line 326)May include surrounding context.

python
if filters:
                for key, value in filters.items():
                    query = query.filter(getattr(self.__model__, key) == value)

            if offset:
                query = query.offset(offset)

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · skills/smyx_common/scripts/dao.py (reported line 441)May include surrounding context.

python
if filters:
                for key, value in filters.items():
                    query = query.filter(getattr(self.__model__, key) == value)

            return query.scalar()
        finally:

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · skills/smyx_common/scripts/dao.py (reported line 353)May include surrounding context.

python
column_names = self.__model__.__table__.columns.keys()

            for key in column_names:
                value = getattr(model, key)
                setattr(instance, key, value)

            session.commit()

ai_chat documents an agent invocation, but its implementation never invokes the agent or produces an analysis result.

Low
Category
Excessive Agency
Confidence
100% confidence
Finding

The docstring at L25-L35 explicitly promises to invoke openclaw agent through subprocess and return an analysis result or session ID. However, the command and subprocess invocation are commented out (L48-L66); the empty dictionary at L68 causes the attribute access at L70 to fail, and the exception handler suppresses the error, leaving the function to return None rather than performing the documented operation.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · skills/smyx_common/scripts/util.py (reported line 30)May include surrounding context.

python
elif isinstance(cur, dict):
                        cur = cur.get(part, default)
                    else:
                        cur = getattr(cur, part)
                return cur
            except Exception:
                return default