Back to skill

Security audit

kami-suspicious-person

Security checks for vulnerabilities and agentic risk

Overview

This surveillance skill is mostly coherent, but it has serious under-scoped handling of biometric snapshots, installation code, credentials, and background monitoring.

Install only after reviewing the privacy and supply-chain implications. Avoid enabling Feishu unless the sm.ms public image-host fallback is removed or explicitly disabled, protect config.json and logs as secrets, use dedicated low-privilege camera and bot credentials, and prefer pinned/verified installers, dependencies, and model files before production use.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
Findings (6)

other

Error
Location
suspicious_person_detector.py:503
Finding

Biometric face snapshots are automatically uploaded to a public third-party image host

Content
View full analysis
str: """Upload a local image to the sm.ms anonymous image host. Returns the public https URL on success, or '' on any failure. sm.ms accepts anonymous multipart uploads (no API key required); per-IP daily quota applies. Used by the Feishu push to obtain a clickable image URL because Feishu custom-bot cards cannot render external image URLs inline. """ if not local_path or not os.path.isfile(local_path): return "" try: with open(local_path, "rb") as f: files = {"smfile": (os.path.basename(local_path), f.read(), "image/jpeg")} resp = requests.post( "https://sm.ms/api/v2/upload", files=files, timeout=15, ) resp.raise_for_status() data = resp.json() if data.get("success") and data.get("data", {}).get("url"): return data["data"]["url"] if data.get("code") == "image_repeated" and data.get("images"): return data["images"] logger.warning(f"sm.ms upload non-success response: {data}") return "" except Exception as e: logger.error(f"Image host upload failed ({local_path}): {e}") return "" ``` ```python if face_local and app_id and app_secret: image_key = _feishu_upload_image(app_id, app_secret, face_local) # Fallback: public image host URL if not image_key and face_local: face_url = _upload_image_to_imghost(face_local) ``` ### Technical Analysis Face snapshots are sensitive biometric and surveillance data. When Feishu notification is enabled, the code first tries to upload the snapshot to Feishu. If application credentials are absent, incomplete, invalid, or the Feishu upload merely fail ...[truncated 2260 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
setup.sh:39
Finding

Setup executes a mutable remote installation script through curl or wget piped directly to a shell

Content
View full analysis
/dev/null; then if command -v curl &> /dev/null; then curl -LsSf https://astral.sh/uv/install.sh | sh elif command -v wget &> /dev/null; then wget -qO- https://astral.sh/uv/install.sh | sh else echo "ERROR: curl or wget is required to install uv." exit 1 fi export PATH="$HOME/.local/bin:$PATH" fi uv python install 3.10 PYTHON_CMD="$(uv python find 3.10)" fi ``` ### Technical Analysis The setup procedure downloads the current contents of `https://astral.sh/uv/install.sh` and immediately executes them with `sh`. The downloaded bytes are not pinned to a reviewed release and are not checked against a cryptographic digest or signature. Astral is a known project, and the script executes without `sudo`, which reduces system-wide impact. Trust in the current domain owner, DNS, TLS/public-key infrastructure, hosting pipeline, and upstream release process nevertheless replaces review of the effective executable payload. The payload can change after this Skill package has been audited. The remote script runs with the full privileges of the user invoking `setup.sh`. Installation of a user-space Python does not inherently require executing an unaudited mutable shell script, so this exceeds the minimum privilege and supply-chain trust necessary for the declared functionality. ### Attack Path 1. The target system lacks an acceptable `python3.10` and does not already have `uv`. 2. The user or agent follows the Skill's mandatory setup workflow and runs `bash setup.sh`. 3. The script retrieves the live installer from `astral.sh`. 4. If the upstream d ...[truncated 1016 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
suspicious_person_detector.py:388
Finding

Face database cache is deserialized with unsafe pickle loading

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
suspicious_person_detector.py:1150
Finding

Downloaded ONNX model archive is neither integrity-verified nor safely extracted

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
suspicious_person_detector.py:1189
Finding

Camera and notification credentials are persisted in plaintext and RTSP credentials are written to logs

Content
View full analysis
dict: cfg_path = os.path.join(script_dir, "config.json") if not os.path.isfile(cfg_path): return {} try: with open(cfg_path, "r", encoding="utf-8") as f: data = json.load(f) or {} return data if isinstance(data, dict) else {} ``` ```python args.feishu_webhook = (args.feishu_webhook or cfg.get("feishu_webhook", "")).strip() args.feishu_secret = (args.feishu_secret or cfg.get("feishu_secret", "")).strip() args.feishu_app_id = (args.feishu_app_id or cfg.get("feishu_app_id", "")).strip() args.feishu_app_secret = (args.feishu_app_secret or cfg.get("feishu_app_secret", "")).strip() args.discord_webhook = (args.discord_webhook or cfg.get("discord_webhook", "")).strip() args.telegram_bot_token = (args.telegram_bot_token or cfg.get("telegram_bot_token", "")).strip() args.telegram_chat_id = (args.telegram_chat_id or cfg.get("telegram_chat_id", "")).strip() ``` ```python logger.info(f"Cameras to monitor ({len(cameras)}):") for cam in cameras: logger.info(f" - {cam['name']}: {cam['rtsp_url']}") ``` The Skill instructions explicitly require user answers, including secrets and RTSP URLs, to be written back to `config.json`. ### Technical Analysis RTSP URLs commonly embed camera usernames and passwords in the authority component, such as `rtsp://user:password@camera/...`. The detector logs each complete URL to `suspicious_person.log`, exposing those credentials to anyone able to read the log. Notification credentials—including Feishu application secrets, webhook URLs, and Telegram bot tokens—are deliberately persisted in plaintext JSON. The code does not create or enforce restrictive file permissions, warn about existing permissive permissions, support enviro ...[truncated 1443 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Python dependencies are installed without version or hash pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (53)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose emphasizes local detection, but the skill also instructs automatic model downloads, outbound alert delivery to third-party services, and persistent storage of snapshots and logs. Omitting these materially important behaviors weakens informed consent and can lead operators to deploy surveillance and data exfiltration paths they did not realize were present.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose emphasizes local detection, but the skill also instructs automatic model downloads, outbound alert delivery to third-party services, and persistent storage of snapshots and logs. Omitting these materially important behaviors weakens informed consent and can lead operators to deploy surveillance and data exfiltration paths they did not realize were present.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

This skill performs face recognition, captures snapshots, stores alerts, and can transmit surveillance data to external services, but the description lacks a clear upfront warning about biometric data collection, retention, and transmission. In a surveillance skill, that omission materially increases privacy and compliance risk because users may not understand they are enabling continuous processing of sensitive personal data.

Content

No source excerpt is available for this finding.

Chaining Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

Piping network-fetched content into sh is effectively command chaining from an untrusted remote source, enabling arbitrary shell execution. In setup scripts this is especially dangerous because users expect installation activity and may not scrutinize what runs.

Content

Scanner excerpt · setup.sh (reported line 44)May include surrounding context.

sh
echo "python3.10 not found. Installing it locally via uv (no sudo required)..."
    if ! command -v uv &> /dev/null; then
        if command -v curl &> /dev/null; then
            curl -LsSf https://astral.sh/uv/install.sh | sh
        elif command -v wget &> /dev/null; then
            wget -qO- https://astral.sh/uv/install.sh | sh
        else

Chaining Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is the same unsafe remote-command execution pattern using wget instead of curl. The shell executes whatever bytes are returned by the remote server, making compromise of that distribution path equivalent to host compromise in the user's context.

Content

Scanner excerpt · setup.sh (reported line 46)May include surrounding context.

sh
if command -v curl &> /dev/null; then
            curl -LsSf https://astral.sh/uv/install.sh | sh
        elif command -v wget &> /dev/null; then
            wget -qO- https://astral.sh/uv/install.sh | sh
        else
            echo "ERROR: curl or wget is required to install uv."
            exit 1

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · setup.sh (reported line 109)May include surrounding context.

sh
fi
    # Move model files to models/ directory
    if [ -d "$EXTRACT_DIR" ]; then
        find "$EXTRACT_DIR" -name '*.onnx' -exec mv {} "$MODELS_DIR/" \;
        rm -rf "$EXTRACT_DIR"
    fi
    rm -f "$TMPZIP"

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code uploads face snapshots to sm.ms, a public third-party image host unrelated to core detection, and returns a public URL. This exposes biometric/surveillance imagery outside the operator’s infrastructure, potentially making sensitive images accessible to unauthorized parties and creating compliance and privacy risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill sends face snapshots to a public anonymous hosting service without any user-facing warning, confirmation, or explicit consent. Because the skill processes unknown-person surveillance data, silent upload of biometric imagery materially increases privacy harm and legal/compliance exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill stores biometric reference images in a face database and saves alert face snapshots, but the README does not warn users about the privacy, legal, and retention implications of collecting and storing facial data. In this context, omission is security-relevant because the skill processes highly sensitive biometric information in a surveillance workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README instructs users to persist RTSP URLs, webhook URLs, bot tokens, and chat identifiers in config.json without warning that these are sensitive secrets and infrastructure details. Leakage of that file could expose internal camera endpoints and enable unauthorized use of notification channels or bots, increasing both privacy and operational risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · README.md (reported line 319)May include surrounding context.

md
4. Add the bot to your target group (or just DM the bot)
5. Get the **chat ID**:
   - DM `@userinfobot` → it replies with your User ID (for private messages)
   - Or call `https://api.telegram.org/bot<TOKEN>/getUpdates` after sending a message in the group → find `"chat":{"id":-100xxxxx}` in the response
   - Group/channel IDs are negative numbers (e.g., `-1001234567890`)

> Push language: **English**

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README expands the skill from one-way surveillance alerting into interactive OpenClaw messaging-channel control paths, including Telegram/Discord/Feishu bot configurations. That materially broadens the attack surface by encouraging users to enable remote command interfaces unrelated to core face-detection functionality, which can expose the host agent to unauthorized triggering or misuse if channel access controls are weak.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill clearly instructs reading and writing local files, making outbound network connections, and executing shell commands, yet it declares no explicit tool scope or permissions boundary. This creates a dangerous mismatch where a host agent may grant broader access than users expect, especially because the skill also persists configuration, downloads models, and launches a daemon automatically.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

Broad natural-language triggers such as 'suspicious person' or 'begin stranger detection' can overlap with ordinary conversation and cause unintended activation of a surveillance workflow. In this skill's context, accidental invocation is more serious because it can lead to camera monitoring, persistent background execution, and collection of biometric data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The text states that push card labels are language-fixed: Feishu in Chinese and Discord/Telegram in English. This imposes a locale choice on users without opt-in or a documented justification that limits the skill to a specific region or audience.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The skill mandates launching as a background daemon with nohup and automatic same-turn startup, which creates persistence outside the immediate session and reduces user visibility and control. In this surveillance context, persistence is especially risky because it can continue monitoring cameras, writing files, and sending alerts long after the initiating conversation ends.

Content

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

md
## Strict Rules (MUST Follow)

- **RULE**: Launch the detector as a background daemon (e.g. `nohup ... &`) so the agent is never blocked.
- **RULE**: Alarms flow via Feishu / Discord / Telegram (all optional) and the inbox file (always). Never tail stdout.
- **RULE**: Every heartbeat consumes `alerts/pending.jsonl`; non-empty → proactive message; empty → `HEARTBEAT_OK`.
- **RULE**: Consumed alarms are MOVED to `alerts/consumed/`, not deleted.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This instruction hard-codes notification label languages by platform, again forcing Chinese for Feishu and English for Discord/Telegram. Because no user language preference or opt-in is offered, it violates the stated language/locale policy criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script serializes biometric face embeddings to face_db.pkl without any warning, consent flow, or protection guidance, even though this data is sensitive and privacy-impacting. In the context of a surveillance skill that detects unknown persons in sensitive areas, silently creating a reusable biometric cache increases the risk of unauthorized retention, copying, or secondary use of personally identifiable biometric data.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
#   2. Use a previously uv-managed python3.10.
#   3. Install uv (single-binary, user-local: ~/.local/bin) and
#      let it install a portable Python 3.10 to
#      ~/.local/share/uv/python/. NO sudo / NO apt required.
# ==============================================================

PYTHON_CMD=""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup.sh (reported line 16)May include surrounding context.

sh
#   2. Use a previously uv-managed python3.10.
#   3. Install uv (single-binary, user-local: ~/.local/bin) and
#      let it install a portable Python 3.10 to
#      ~/.local/share/uv/python/. NO sudo / NO apt required.
# ==============================================================

PYTHON_CMD=""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup.sh (reported line 41)May include surrounding context.

sh
#   2. Use a previously uv-managed python3.10.
#   3. Install uv (single-binary, user-local: ~/.local/bin) and
#      let it install a portable Python 3.10 to
#      ~/.local/share/uv/python/. NO sudo / NO apt required.
# ==============================================================

PYTHON_CMD=""

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script fetches a remote installer over the network and immediately pipes it to sh, giving the remote endpoint full code-execution capability in the user's context. If the upstream host, CDN, TLS trust chain, or download path is compromised, arbitrary commands can run during setup without review.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

This setup path transmits data to an external domain to retrieve and install uv, creating a supply-chain and outbound-network dependency. Although expected for bootstrapping, it is still security-relevant because it executes software obtained from the network.

Content

Scanner excerpt · setup.sh (reported line 48)May include surrounding context.

sh
elif command -v wget &> /dev/null; then
            wget -qO- https://astral.sh/uv/install.sh | sh
        else
            echo "ERROR: curl or wget is required to install uv."
            exit 1
        fi
        export PATH="$HOME/.local/bin:$PATH"

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The script reaches out to an external host to download model archives, which introduces supply-chain risk and undeclared network transfer. If that host serves altered content, the skill could consume malicious or poisoned artifacts.

Content

Scanner excerpt · setup.sh (reported line 82)May include surrounding context.

sh
if [ ! -f "$DET_MODEL" ] || [ ! -f "$REC_MODEL" ]; then
    echo "Downloading face detection and recognition models..."
    TMPZIP=$(mktemp /tmp/kami_model_XXXXXX.zip)
    if command -v curl &> /dev/null; then
        curl -L -o "$TMPZIP" "$MODEL_URL"
    elif command -v wget &> /dev/null; then
        wget -O "$TMPZIP" "$MODEL_URL"

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The code deserializes face_db.pkl with pickle.load(), which allows arbitrary code execution if the pickle file is replaced or tampered with. In this skill, the pickle is loaded automatically from a predictable local path during startup, so any attacker who can write to the skill directory or supply the database can achieve code execution in the detector process.

Content

Scanner excerpt · suspicious_person_detector.py (reported line 396)May include surrounding context.

python
pkl_path = os.path.join(db_path, "face_db.pkl")
        if os.path.exists(pkl_path):
            with open(pkl_path, "rb") as f:
                self.registered_faces = pickle.load(f)
            logger.info(f"Loaded face database from pkl: {len(self.registered_faces)} records")
            return

Static analysis

No suspicious patterns detected.