Back to skill

Security audit

Airplane AI / 断网 AI 助手

Security checks for vulnerabilities and agentic risk

Overview

This skill is an offline chat UI, but it lets model output automatically read arbitrary local files and can send chat or file content to configurable endpoints despite strong privacy claims.

Review carefully before installing. Use only with a trusted local LLM endpoint, keep the server bound to 127.0.0.1, and do not use the file-read feature unless you are comfortable with file contents being placed into the chat context. Avoid pointing --llm-url at a remote or untrusted endpoint because prompts, persona text, and automatically read file snippets can be sent there.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/offline_chat.py:231
Finding

Model-Triggered Arbitrary Local File Disclosure

Content
View full analysis
> — the system will feed content back.""" ``` ```python def read_file(filepath): path = Path(filepath).expanduser().resolve() if not path.exists(): return {"error": f"File not found: {filepath}"} try: content = path.read_text(encoding="utf-8") return {"content": content, "size": len(content)} except Exception as e: return {"error": str(e)} ``` ```python if "/api/read" in first: try: req = json.loads(body) result = read_file(req.get("path", "")) resp = json.dumps(result, ensure_ascii=False) conn.sendall(f"HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: {len(resp.encode())}\r\n\r\n{resp}".encode()) except Exception as e: err = json.dumps({"error": str(e)}, ensure_ascii=False) conn.sendall(f"HTTP/1.1 500 OK\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: {len(err.encode())}\r\n\r\n{err}".encode()) return ``` The browser also automatically interprets a model-generated directive and submits it to this endpoint: ```javascript let read_match=d.content.match(/<>/); if(read_match){ let fp=read_match[1].trim(); let fr=await fetch("/api/read",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({path:fp}) }); let fd=await fr.json(); if(fd.content){ let truncated=fd.content.length>4000 ? fd.content.substring(0,4000)+"\n... [truncated, ...[truncated 2893 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/offline_chat.py:218
Finding

Unrestricted LLM Endpoint Can Transmit Chats, Persona Data, and Local File Content Externally

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/offline_chat.py:131
Finding

HTML Injection Through Unescaped Model and Environment-Controlled Values

Content
View full analysis
{title} ... <div class="header"> <span class="status-dot online" id="statusDot" title="Connected"></span> <b>🛫 AirplaneAI</b> <span style="font-size:11px;color:#888" id="statusText">Connected</span> <span class="model-tag">{llm_model_display}</span> </div> <div class="chat" id="chat"> <div class="msg system">🛫 AirplaneAI ready — chatting with {llm_model_display} via {llm_url_display}</div> </div> ... """ ``` The model value can originate from an HTTP response: ```python def detect_model(llm_url): base = re.sub(r"/v1/chat/completions$", "", llm_url.rstrip("/")) try: req = urllib.request.Request( f"{base}/v1/models", headers={"Accept": "application/json"} ) with urllib.request.urlopen(req, timeout=5) as r: data = json.loads(r.read()) models = [m["id"] for m in data.get("data", []) if isinstance(m, dict)] return models[0] if models else None except Exception: return None ``` ### Technical Analysis `build_page` interpolates `title`, `llm_model_display`, and `llm_url_display` directly into HTML without escaping. The model identifier can be supplied by the configured LLM server through `/v1/models`, while the title and URL can be influenced through environment variables or command-line configuration. A malicious model identifier containing HTML or JavaScript is therefore interpreted as ac ...[truncated 1881 chars]
Remediation
View remediation

other

Note
Location
scripts/offline_chat.py:89
Finding

Health Check Makes an Unnecessary External Request to ClawHub

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (27)

Tainted flow: 'req' from os.environ.get (line 209, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/offline_chat.py (reported line 53)May include surrounding context.

python
# LLM endpoint
    try:
        req = urllib.request.Request(f"{base}/v1/models", headers={"Accept": "application/json"})
        with urllib.request.urlopen(req, timeout=5) as r:
            data = json.loads(r.read())
            models = [m["id"] for m in data.get("data", [])]
        print(f"✅ LLM reachable: {base}/v1/models")

Tainted flow: 'req' from os.environ.get (line 209, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/offline_chat.py (reported line 104)May include surrounding context.

python
# LLM endpoint
    try:
        req = urllib.request.Request(f"{base}/v1/models", headers={"Accept": "application/json"})
        with urllib.request.urlopen(req, timeout=5) as r:
            data = json.loads(r.read())
            models = [m["id"] for m in data.get("data", [])]
        print(f"✅ LLM reachable: {base}/v1/models")

Tainted flow: 'req2' from os.environ.get (line 73, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/offline_chat.py (reported line 74)May include surrounding context.

python
"max_tokens": 5
        }).encode()
        req2 = urllib.request.Request(llm_url, data=test_body, headers={"Content-Type": "application/json"})
        with urllib.request.urlopen(req2, timeout=10) as r:
            json.loads(r.read())
        print(f"✅ Chat API working: {llm_url}")
    except Exception as e:

Tainted flow: 'req' from os.environ.get (line 209, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/offline_chat.py (reported line 210)May include surrounding context.

python
"max_tokens": MAX_TOKENS,
    }, ensure_ascii=False).encode()
    req = urllib.request.Request(llm_url, data=body, headers={"Content-Type": "application/json"}, method="POST")
    with urllib.request.urlopen(req, timeout=120) as r:
        return json.loads(r.read())["choices"][0]["message"]["content"]

Tainted flow: 'req' from os.environ.get (line 260, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/offline_chat.py (reported line 238)May include surrounding context.

python
# Health check from UI
            try:
                req = urllib.request.Request(f"{re.sub(r'/v1/chat/completions$','',llm_url.rstrip('/'))}/v1/models")
                with urllib.request.urlopen(req, timeout=3):
                    conn.sendall(b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{\"ok\":true}")
            except Exception:
                conn.sendall(b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{\"ok\":false}")

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding

Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill is presented as an offline local chat UI, but the documented behavior includes model-directed arbitrary local file reads and WiFi/offline verification network activity not reflected in the stated purpose. That mismatch is dangerous because users may trust the tool with sensitive local data under the assumption that it only proxies chat to a local LLM, while the model can induce additional file access and context ingestion.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The persona advertises local filesystem access without warning users that prompts may expose secrets or private files to the model context. Because read content is automatically fed back to the assistant, users may unknowingly disclose credentials, personal data, or proprietary material simply by following the assistant's suggestions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill’s stated purpose is a local chat UI, but the system prompt grants the model arbitrary file-read capability via <READ:/path/to/file>. This expands scope from chat to local data access and enables prompt-driven exposure of sensitive files unrelated to the user’s request.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The system prompt explicitly teaches the model that it can request arbitrary local files and that the system will feed the contents back. This creates an LLM-driven data access channel where prompt injection, model misbehavior, or adversarial instructions can cause sensitive file exfiltration into the conversation and then to the LLM backend.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
100% confidence
Finding

The frontend automatically parses model output for <READ:...>, reads the requested file through /api/read, and injects the content back into the chat without confirmation. This lets the model unilaterally trigger local file access, making prompt injection and accidental disclosure especially dangerous in a tool that is supposed to be a simple offline chat UI.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The server exposes a browser-accessible /api/read endpoint that returns contents of any requested local path. This is far broader than a chat interface and creates a direct local file disclosure surface to any page or client that can reach the service.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The natural-language instructions in this file are entirely in Chinese, and there is no indication that users can choose another language or that the skill is intentionally restricted to a Chinese-speaking audience for a documented reason. Under the stated policy, forcing a specific language without user opt-in can be a locale-policy violation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README makes strong privacy/offline assurances ('完全本地运行', '不上传聊天内容') while later documenting support for any OpenAI-compatible API endpoint. That creates a misleading security boundary: users may trust the tool with sensitive data believing it cannot leave the machine, even though configuration can direct prompts and file-derived content to remote services.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The documented behavior allows the AI output token <<READ:/路径>> to trigger automatic reading of local files and appending their contents into the chat. For a browser chat UI, this materially expands the trust model from simple local inference to agentic file access, enabling prompt-driven exfiltration of sensitive files into the conversation and potentially onward to any configured backend.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Automatic reading of arbitrary local file paths is a genuine security-sensitive capability because it can be steered by model output rather than direct user intent. In this skill's context, the danger is heightened by the product's privacy-focused positioning, which may lead users to expose local documents under the assumption the tool is only a passive offline chat frontend.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Describing automatic local file reading without a clear privacy warning is dangerous because users may not realize chats can silently absorb sensitive local content. If the backend is misconfigured to a remote endpoint, or logs are retained, the exposure can extend beyond the local machine despite the README's privacy-oriented framing.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documents capabilities that imply local file access and network communication, but it does not declare any tool scope or permissions boundaries. That creates a transparency and governance gap: users and orchestrators cannot accurately constrain or review what the skill may do, increasing the chance of unintended file exposure or network activity.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation claims privacy because data stays on the user's computer, yet it also describes an automatic mechanism where model output can trigger reading arbitrary local files into the chat context. Even without exfiltration to the internet, this can expose secrets, SSH keys, tokens, personal documents, or other sensitive local content to the model and any UI/session logs.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Allowing the model to request arbitrary local files is unnecessary for the core purpose of providing an offline browser chat interface and significantly broadens the attack surface. A prompt-injected or merely overeager model could ask for unrelated files, causing confidentiality breaches far beyond what users expect from a chat wrapper.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill describes automatic file-reading behavior without warning users that local file contents may be inserted into model context. Missing that warning undermines informed consent and increases the likelihood that users will unknowingly expose sensitive local data during ordinary chat interactions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The persona explicitly instructs the model that it can read arbitrary local files by emitting a special token, and it states the feature works anywhere on the filesystem. That creates a broad prompt-exposed capability unrelated to a basic offline chat persona and can be triggered through normal conversation, enabling exfiltration of sensitive local data such as SSH keys, tokens, browser data, or private documents.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file-read mechanism is described as generally available with no trigger policy, path restrictions, or safety exclusions. In practice, this means any prompt injection, social engineering prompt, or accidental model behavior could cause reads from unintended locations on disk, greatly increasing the chance of sensitive data exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The module docstring labels the assistant as a Chinese-language variant ("断网离线 AI 助手 (小白友好版)"), and the generated HTML hard-codes Chinese locale/content without any opt-in or alternative. This constitutes a natural-language locale constraint that is not documented as region-specific or user-selectable.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The product text and UI do not prominently warn users that the model can request local file reads and that the application will service those requests. This undermines informed consent and increases the chance users unknowingly expose sensitive local data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.