Back to skill

Security audit

Telegram File Browser

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Telegram file browser, but it needs Review because its callback handling and file-transfer paths can expose local files or run unsafe commands.

Install only if you are comfortable giving the skill access to browse and send files from the OpenClaw workspace through Telegram. It should be fixed before normal use: callbacks should be passed as safely quoted arguments or argv, callback tokens should be bound to the active session/version, preview/download should revalidate resolved paths against the root and handle symlinks safely, and file downloads should require clear user intent.

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)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:112
Finding

Shell Command Injection Through Unquoted Callback Concatenation

Content
View full analysis
` immediately ``` ### Technical Analysis The Skill instructs an agent to concatenate attacker-influenced `callback_data` directly into a command string passed to the `exec` tool. No shell quoting or complete callback validation is applied before constructing the command. The documented detection expression only verifies that inbound text starts with an accepted prefix. It is not anchored at the end and does not prevent shell metacharacters, substitutions, redirections, or additional commands from appearing after the valid-looking prefix. If the OpenClaw `exec` tool evaluates the supplied command through a shell, characters such as command separators or command substitutions in the inbound callback can be interpreted by that shell rather than being passed only as an argument to `browser_dispatcher.py`. The Python implementation itself uses argument arrays when launching its child processes, but that protection does not cover the vulnerable command construction prescribed by `SKILL.md`. ### Attack Path 1. An attacker sends a Telegram text message beginning with a recognized callback prefix, such as `tfb_preview_`. 2. The message appends shell syntax after the accepted prefix. 3. Following the Skill instructions, the agent treats the entire inbound message as callback data. 4. The agent concatenates that data into the `exec(command="...") ...[truncated 838 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/build_view.py:29
Finding

Workspace Root Boundary Bypass Through File Symlinks

Content
View full analysis
List[Dict[str, str]]: items = [] for i, entry in enumerate(entries, start=start_index): items.append({ "id": f"{prefix}{i}", "name": entry.name, "path": str(entry), "type": "dir" if entry.is_dir() else "file" }) return items ``` File actions are opened without resolving the target or confirming that it remains under the configured root: ```python def open_file_actions(state: Dict[str, Any], item_id: str) -> Dict[str, Any]: item = find_item(state, item_id) if not item or item.get("type") != "file": raise ValueError(f"File item not found: {item_id}") state["selectedFileId"] = item_id state["selectedFilePath"] = item["path"] bump_menu_version(state) ``` Preview and download operations trust the stored item path: ```python if action == 'preview': item = resolution.get('item') if not item: return {'toolAction': 'noop', 'message': 'preview target not found', 'state': state} preview = run_json(['python3', str(PREVIEW), item['path']]) ``` ```python if action == 'download': item = resolution.get('item') if not item: return {'toolAction': 'noop', 'message': 'download target not found', 'state': state} path = Path(item['path']) if not path.exists() or path.is_dir(): return { 'toolAction': 'send', 'message': f"⚠️ 无法下载:目标不存在或是目录\n{path_label(item['path'])}", 'replyTo': str(live_message_id), 'state': load_state(s ...[truncated 2906 chars]
Remediation
View remediation
Path: root = Path(root_value).expanduser().resolve(strict=True) target = Path(target_value).expanduser().resolve(strict=True) if not target.is_relative_to(root): raise PermissionError("Target escapes configured root") return target ``` 2. Pass the configured root to `preview_file.py` and validate the resolved target there as defense in depth. 3. Resolve and validate download targets immediately before constructing the message-tool payload. 4. Decide on an explicit symlink policy. The safest option is to reject all symlinks using `is_symlink()` and avoid following symlinked path components. 5. Revalidate authorization at the time of use because files and links can change after directory enumeration. 6. Validate path and file-type operations against the same resolved object. 7. Avoid relying on state-stored paths as authorization evidence; treat persistent state as untrusted input. 8. Add tests for file symlinks, directory symlinks, chained symlinks, broken links, and targets replaced after listing. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/resolve_callback.py:30
Finding

Stale Item Callbacks Bypass Menu-Version Validation

Content
View full analysis
Optional[dict]: for view in state.get("views", {}).values(): for item in view.get("items", []): if item.get("id") == item_id: return item return None ``` ### Technical Analysis Item callbacks are parsed and resolved before the general menu-version validation is performed. The callback's version component is accepted as `\d+` but never compared with `state["menuVersion"]` on this execution path. The code explicitly returns the result with the note `processed despite version mismatch`. This contradicts the Skill's documented requirement to reject stale callbacks. The risk is increased by globally searching every retained view for an item ID. Item identifiers are short and are not globally unique: root IDs use positional values such as `w1`, while directory prefixes are generated elsewhere from a path hash reduced modulo 1000. Consequently, retained views can contain repeated or colliding identifiers, and a stale callback may reso ...[truncated 1432 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/preview_file.py:12
Finding

Unbounded Full-File Reads Allow Memory Exhaustion During Preview

Content
View full analysis
bool: if path.suffix.lower() in TEXT_EXTENSIONS: return True try: chunk = path.read_bytes()[:1024] except Exception: return False return b"\x00" not in chunk ``` ```python text = path.read_text(errors="replace") lines = text.splitlines() clipped_lines = lines[:args.max_lines] clipped_text = "\n".join(clipped_lines) truncated = False if len(lines) > args.max_lines: truncated = True if len(clipped_text) > args.max_chars: clipped_text = clipped_text[:args.max_chars] truncated = True ``` ### Technical Analysis The apparent preview bounds are applied only after the entire file has been loaded and split into lines. `path.read_bytes()[:1024]` does not read only 1,024 bytes. `read_bytes()` first loads the complete file, after which slicing retains the first 1,024 bytes. Similarly, `read_text()` loads and decodes the complete file before the line and character limits are applied. `splitlines()` can then allocate further memory proportional to the file size. Files with recognized text extensions skip the initial binary check but are still fully read. Therefore, the `--max-lines` and `--max-chars` options constrain only the returned output, not memory or processing consumption. ### Attack Path 1. An attacker places a very large file within the browsable workspace or causes such a file to be available through an accessible path. 2. The browser lists the file. 3. A user or agent selects the preview action. 4. `preview_file.py` loads the entire file into memory. 5. Text decoding and `splitlines()` create additional allocations. 6. The preview subprocess consumes excessive memory and CPU. 7. The process may terminate, become unresponsive, or ...[truncated 599 chars]
Remediation
View remediation
MAX_PREVIEW_BYTES raw = raw[:MAX_PREVIEW_BYTES] text = raw.decode("utf-8", errors="replace") ``` 2. Use a bounded read for textual detection rather than `read_bytes()`. 3. Check file size with `stat()` and reject or provide metadata-only output for files exceeding a configured threshold. 4. Limit line processing while streaming instead of calling `splitlines()` on the entire file. 5. Apply subprocess timeouts and operating-system memory limits as defense in depth. 6. Handle special files such as FIFOs, devices, and sockets safely; regular-file checks should occur before reading. 7. Add tests using sparse files, large single-line files, large multi-line files, and files with misleading text extensions. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill includes generic JSON-plan validation and command-line dispatch behavior that is broader than a simple Telegram browser UI description suggests. Even if intended as implementation detail, this broadens the operational surface and may enable unreviewed message construction or shell-driven flows beyond what the declared purpose communicates.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill includes generic JSON-plan validation and command-line dispatch behavior that is broader than a simple Telegram browser UI description suggests. Even if intended as implementation detail, this broadens the operational surface and may enable unreviewed message construction or shell-driven flows beyond what the declared purpose communicates.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill clearly instructs use of shell execution, local state files, and filesystem-oriented scripts, but it does not declare any explicit tool scope or allowed-tools boundary. That omission weakens reviewability and can let an agent invoke broader file and shell capabilities than users or platform policy may expect.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
3. Use `payload['message']` and `payload['buttons']` from the validated result

**Common mistakes to avoid:**
- ❌ Don't rewrite buttons manually
- ❌ Don't flatten buttons into one row
- ❌ Don't pass buttons as a JSON string

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

The skill tells the agent to automatically treat matching inbound text as callback data and execute a shell-dispatched action immediately without user confirmation. In context, that can trigger filesystem navigation or file-related actions from crafted text that only resembles an internal callback, increasing the risk of unintended autonomous execution.

Content

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

md
- if inbound text matches `^tfb_(root|dir|preview|path|download|back|close)_`
- treat it as a telegram-file-browser callback
- run `browser_dispatcher.py handle-callback <that_text>` immediately
- do not ask the user what they mean

---

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The recommended action menu hard-codes Chinese labels such as 预览 and 下载, and later pagination controls also use Chinese text. This imposes a specific language on users without opt-in or justification, which is a natural-language locale policy issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This markdown file instructs the skill to send a local file as a Telegram attachment, which can affect user data privacy and move local content into an external messaging platform. The interaction pattern specifies the action flow but does not include any warning or disclosure about transmitting file contents off-host.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code presents the browser UI text in Chinese only, which enforces a specific language on all users. The policy for natural-language violations applies to all file types, and there is no opt-in, fallback, or documented justification for the locale restriction in this file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file action menu text is fixed in Chinese, which can violate language/locale policy when users are not given a choice. Nothing in this file indicates that the skill is region-specific or that Chinese output is optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This code hard-codes multiple user-visible labels in Chinese, such as pagination and navigation buttons, without offering a language choice or documenting that the skill is intentionally Chinese-only. That creates a natural-language locale policy issue because the skill imposes a specific language on all users.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The resolver explicitly supports a download action even though the skill metadata describes browsing, paging, previewing, and path exposure rather than file transfer. This scope expansion can enable unintended file exfiltration if another component trusts this resolver's output and permits downloads for files present in state, especially since the script already tolerates some version mismatches for item-based actions.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/run_browser_action.py (reported line 19)May include surrounding context.

python
def run_json(cmd: list[str]) -> Dict[str, Any]:
    out = subprocess.check_output(cmd, text=True)
    return json.loads(out)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code previews file contents and transmits file paths/files without any visible consent, warning, or disclosure in the user flow shown here. In a Telegram-integrated browser, silent preview/export features can expose sensitive local data more easily than users may expect.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Several natural-language strings shown to the user are hardcoded in Chinese, such as truncation and download error messages, with no indication that the skill is region-specific or that users can choose a locale. This can violate language/locale policy when a skill imposes a language without opt-in.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill goes beyond inline browsing/navigation and includes active file exfiltration behavior via Telegram send-file responses. That materially expands capability from UI navigation into data transfer, which increases the risk of sensitive local files being exposed through chat interactions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This code can return arbitrary local files to Telegram by taking a resolved item path and emitting a send-file action with that path. In a file-browser context, this is dangerous because any weakness in path scoping or callback resolution elsewhere could turn browsing into straightforward local file exfiltration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instruction explicitly says not to reply with the Chinese phrase “你想让我做什么?”, which is a language-specific policy embedded in the skill text. This can create a locale/language handling constraint without documenting user choice or opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The pagination controls use Chinese labels like 上一页, 下一页, and 上级目录 with no indication that the user can choose a different language. This continues the same locale constraint in another user-facing part of the interaction design.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code persists runtime state by creating directories and writing JSON to a file, which is a file write operation covered by the warning requirement for code files. The function contains no confirmation prompt, user-facing log/print, or explanatory comment/docstring disclosing that user state will be stored on disk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The helper runs external Python scripts via subprocess.check_output, which is a safety-relevant operation for code files. While the execution appears central to the skill's implementation, this file provides no confirmation prompt, logging, or comment/docstring disclosing that callbacks and browser actions are handled by spawning subprocesses.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.