Back to skill

Security audit

Ai Interview

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent AI interview purpose, but its web viewer can expose and delete local OpenClaw conversation logs without proper access controls.

Review or fix server.py before installing. Keep the viewer bound to localhost only, do not expose port 8091 to a network, restrict access to the two intended agents, remove or protect the clear endpoint, and store Feishu app secrets carefully outside version control with a rotation plan.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.py:145
Finding
Unauthenticated Network Exposure of Agent Session Conversations<![CDATA[ ## Vulnerability Details **File Location**: `server.py`, lines 17-26, 145-198, and 234-240 **Vulnerability Type**: Missing authentication and excessive data exposure **Risk Level**: High ### Vulnerable Code ```python def get_available_agents(): """获取所有可用的 agent""" agents_dir = SESSION_DIR if not agents_dir.exists(): return [] agents = [] for item in agents_dir.iterdir(): if item.is_dir() and (item / "sessions").exists(): agents.append(item.name) return sorted(agents) ``` ```python if path == '/api/agents': # 返回可用 agent 列表 agents = get_available_agents() self.send_response(200) self.send_header('Content-type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.end_headers() self.wfile.write(json.dumps({'agents': agents}).encode()) elif path == '/api/conversations': # 返回对话 agent1 = params.get('agent1', [''])[0] agent2 = params.get('agent2', [''])[0] if not agent1 or not agent2: self.send_response(400) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'error': 'Missing agent1 or agent2'}).encode()) return convos = get_conversations(agent1, agent2) # 合并消息 all_msgs = [] msgs1 = convos.get('agent-1', []) msgs2 = convos.get('agent-2', []) max_len = max(len(msgs1), len(msgs2)) for i in range(max_len): if i < len(msgs2): all_msgs.append({ 'from': 'agent-2', 'content': msgs2[i]['content'], 'time': msgs2[i]['time'] }) if i < len(msgs1): all_msgs.append({ 'from': 'agent-1', 'content': msgs1[i]['content'], 'time': msgs1[i]['time'] }) self.send_response(200) self.send_header('Content-type', 'application/json') self.send_header('Access-Control-Allow ...[truncated 2886 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind the viewer exclusively to the loopback interface: ```python server = HTTPServer(('127.0.0.1', PORT), Handler) ``` 2. Require authenticated requests using a securely generated session or bearer token. Compare authentication values using a constant-time comparison where applicable. 3. Enforce server-side authorization. Restrict the viewer to an explicit allowlist containing only `job-seeker` and `recruiter`. 4. Do not use `Access-Control-Allow-Origin: *`. If cross-origin access is necessary, permit only an explicitly configured trusted origin and reject untrusted `Origin` values. 5. Return only the fields required by the viewer and redact secrets or sensitive metadata from session content. 6. Add rate limiting and security logging for agent enumeration and conversation access. 7. Document that session conversations are sensitive local data and that the viewer must not be exposed through a public interface or untrusted reverse proxy. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.py:29
Finding
Filesystem Path Traversal Through Unvalidated Agent Names<![CDATA[ ## Vulnerability Details **File Location**: `server.py`, lines 29-40 and 124-140 **Vulnerability Type**: Path traversal and missing filesystem confinement **Risk Level**: Critical ### Vulnerable Code ```python def get_latest_session(agent_name): """获取最新的 session 文件""" agent_dir = SESSION_DIR / agent_name / "sessions" if not agent_dir.exists(): return None sessions = list(agent_dir.glob("*.jsonl")) if not sessions: return None latest = max(sessions, key=lambda p: p.stat().st_mtime) return latest ``` ```python def clear_conversations(agent1, agent2): """清空指定 agent 的对话记录""" cleared = [] for agent in [agent1, agent2]: agent_dir = SESSION_DIR / agent / "sessions" if agent_dir.exists(): sessions = list(agent_dir.glob("*.jsonl")) for s in sessions: try: s.unlink() cleared.append(agent) print(f"已删除 {agent} 的会话: {s.name}") except Exception as e: print(f"删除失败: {e}") return cleared ``` The affected values originate from request query parameters: ```python agent1 = params.get('agent1', [''])[0] agent2 = params.get('agent2', [''])[0] ``` ### Technical Analysis The application directly inserts attacker-controlled `agent1` and `agent2` strings into filesystem paths. It does not reject absolute paths, path separators, or `..` traversal components. With `pathlib`, joining an absolute path can discard the intended base path. Relative traversal components can also cause the normalized target to resolve outside `~/.openclaw/agents`. The application never resolves the resulting path and verifies that it remains beneath `SESSION_DIR`. The selected target must contain a `sessions` child directory and matching `*.jsonl` files, but this restriction does not provide a security boundary. Any such directory readable or writable by the server process can beco ...[truncated 1880 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not treat request values as filesystem paths. Accept only logical agent identifiers obtained from a server-side allowlist. 2. Restrict identifiers to a conservative format, such as: ```python import re if not re.fullmatch(r"[A-Za-z0-9_-]+", agent_name): raise ValueError("Invalid agent identifier") ``` 3. Resolve and verify every derived path before accessing it: ```python base = SESSION_DIR.resolve() target = (base / agent_name / "sessions").resolve() if base not in target.parents: raise ValueError("Path escapes agent directory") ``` 4. Explicitly reject absolute paths, `..`, slash characters, backslashes, URL-encoded separators after decoding, and null bytes. 5. Verify that the selected identifier is present in the authorized-agent set, rather than merely checking that its directory exists. 6. Apply the same canonicalization and containment checks to read and deletion operations. 7. Run the viewer under a dedicated low-privilege account with access only to the two required interview-session directories. 8. Add automated tests covering absolute paths, nested traversal, encoded traversal, symlink escape, nonexistent agents, and unauthorized valid agent names. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.py:206
Finding
Unauthenticated Destructive Session Deletion Through a GET Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `server.py`, lines 124-140 and 206-224 **Vulnerability Type**: Missing authorization, unsafe HTTP method, and cross-site request forgery exposure **Risk Level**: Critical ### Vulnerable Code ```python def clear_conversations(agent1, agent2): """清空指定 agent 的对话记录""" cleared = [] for agent in [agent1, agent2]: agent_dir = SESSION_DIR / agent / "sessions" if agent_dir.exists(): sessions = list(agent_dir.glob("*.jsonl")) for s in sessions: try: s.unlink() cleared.append(agent) print(f"已删除 {agent} 的会话: {s.name}") except Exception as e: print(f"删除失败: {e}") return cleared ``` ```python elif path == '/api/clear': # 清空对话 agent1 = params.get('agent1', [''])[0] agent2 = params.get('agent2', [''])[0] if not agent1 or not agent2: self.send_response(400) self.send_header('Content-type', 'application/json') self.end_headers() self.wfile.write(json.dumps({'error': 'Missing agent1 or agent2'}).encode()) return cleared = clear_conversations(agent1, agent2) self.send_response(200) self.send_header('Content-type', 'application/json') self.send_header('Access-Control-Allow-Origin', '*') self.end_headers() self.wfile.write(json.dumps({ 'success': True, 'cleared': cleared }).encode()) ``` ### Technical Analysis The `/api/clear` endpoint performs a permanent destructive operation without authentication or authorization. It accepts agent names from query parameters and deletes every `*.jsonl` session file associated with both values. The operation is exposed through HTTP GET. GET requests are expected to be safe and non-state-changing, and they can be generated by links, images, browser prefetching, crawlers, and cross-origin page elements. Because the endpoin ...[truncated 1956 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove session deletion from the viewer unless it is essential to the declared functionality. 2. Require authentication and verify that the authenticated user is authorized to delete each selected agent's sessions. 3. Use `DELETE` or a protected `POST` operation instead of GET. 4. Require a server-generated anti-CSRF token and validate `Origin` or `Referer` against an explicit trusted origin. 5. Restrict deletion to a fixed allowlist of interview agents and apply canonical path-containment checks. 6. Require a short-lived confirmation token bound to the exact agent IDs and deletion action. 7. Replace permanent unlinking with a recoverable archive or quarantine operation. Apply retention controls and allow restoration. 8. Return an error if either agent is unauthorized; do not silently process partial deletion requests. 9. Record authenticated deletion events, including actor, time, selected agents, and outcome, without logging conversation content or secrets. 10. Bind the service to `127.0.0.1` by default and require explicit secure configuration before allowing remote access. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tp1

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Zero-width Unicode characters are invisible to humans but detectable by AI. When followed by visible text, they indicate hidden content injection.

Tp1

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Zero-width Unicode characters are invisible to humans but detectable by AI. When followed by visible text, they indicate hidden content injection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documentation markets the skill as an AI interview system with Feishu bots and MoonShot integration, but the broader analysis indicates the implemented behavior includes undeclared local session-file read/delete functionality while omitting core declared features. A mismatch like this is dangerous because users may install or run the skill under false assumptions, exposing local data or destructive actions not disclosed by the manifest.

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
Content
---
name: ai-interview
description: |
  🤖 AI 面试系统 - 完整的 AI 面试解决方案
  
  提供求职者和面试官两个 AI Agent,支持飞书群聊面试 + 实时可视化观察。
  
  **功能:**
  - 👨‍💻 job-seeker - AI 求职者(3年前端,微前端经验)
  - 👨‍💼 recruiter - AI 面试官(提问、评估候选人)
  - 📊 web-viewer - 实时可视化观察面板
  
  **依赖:**
  - 飞书应用 x2(job-seeker + recruiter)
  - MoonShot API Key
  
  **触发词:**
  - "安装 ai-interview"
  - "安装面试系统"
  - "ai-interview"
metadata:
  version: "1.0.0"
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The /api/clear endpoint performs remote deletion of agent session .jsonl files with no authentication, no authorization, and permissive CORS headers elsewhere in the API. Because these logs likely contain conversation history and evidence of agent behavior, unauthenticated deletion can destroy audit trails and interfere with monitoring or incident investigation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill declares operational capabilities that imply file reading and network use, but it does not specify any tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where an agent or runtime may grant broader access than users expect, especially since the documentation also instructs users to run local scripts and a Python server.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The natural-language content, including description, setup steps, and usage instructions, is exclusively in Chinese. Under the policy rule, forcing a specific language without user opt-in or documented justification is a locale/language policy concern.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill instructs users to place Feishu App Secrets directly into a local configuration file without any warning about secret handling, storage protections, or rotation. This increases the chance of credential leakage through file exposure, backups, screenshots, version control, or overly broad filesystem access by other tools.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The file’s entire identity and behavior instructions are written only in Chinese and define the agent persona and response behavior without offering any user language choice or opt-in. Under the policy for natural-language violations, forcing a specific language/locale without user choice is in scope.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The file’s natural-language instructions are entirely written in Chinese and define the agent persona and interview flow without offering any language or locale choice. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The document declares `lang="zh-CN"`, and the visible UI text throughout the page is Chinese, which indicates the skill is fixed to a specific language/locale. There is no visible user opt-in, language selector, or documentation that this is intentionally limited to a China-specific context, so this is a natural-language locale policy concern.

Session Persistence

Medium
Category
Rogue Agent
Content
# 创建工作空间
echo "📁 创建 Agent 工作空间..."
mkdir -p ~/.openclaw/workspace-job-seeker
mkdir -p ~/.openclaw/workspace-recruiter

# 复制配置模板
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.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The web viewer is described as an observation panel, but the backend also exposes a destructive capability that deletes session logs for selected agents. This creates an unexpected integrity risk because anyone who can reach the server can trigger deletion via a simple GET request, undermining auditability and observability.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
A destructive endpoint is exposed without any confirmation, warning, or secondary check, so accidental or scripted requests can immediately erase session files. This increases the likelihood of both misuse and operator error, especially in a tool framed as a passive real-time viewer.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The script's user-facing comments and prompts are presented in Chinese only, including the installation guidance and interactive prompt. This can violate a language/locale policy when a skill forces a specific language without opt-in or alternative locale support.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The file-level natural-language description is entirely in Chinese and presents the service purpose only in that language, with no indication that users may choose another language. This can violate language/locale policy when a skill implicitly constrains interaction or documentation to a single language without opt-in or justification.

Static analysis

No suspicious patterns detected.