Back to skill

Security audit

smart-classroom-teaching

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent classroom workbench, but it exposes student/classroom state through unsafe local rendering and an unauthenticated localhost API.

Review before installing. Use the workbench only with trusted lesson JSON and trusted state sources, avoid putting identifiable student data into it, stop the local server when not actively using it, and do not browse untrusted websites while the unauthenticated localhost service is running. The publisher should add schema validation, sanitize or remove arbitrary HTML/SVG rendering, restrict CORS, add per-session authorization, limit request sizes, and clarify privacy/data-retention guidance.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
assets/smart-classroom-workbench.html:428
Finding
Stored DOM-Based Cross-Site Scripting Through Untrusted Classroom State<![CDATA[ ## Vulnerability Details **File Location**: `assets/smart-classroom-workbench.html`, lines 428-444; additional entry points at lines 471-483 and 573-584 **Vulnerability Type**: Stored DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code ```javascript function paint(){ const s = history[cursor]; if(!s) return; document.getElementById("topic").textContent = s.topic; document.getElementById("boardBody").innerHTML = renderBoard(s.board); document.getElementById("boardCaption").textContent = (s.board && s.board.caption) || ""; document.getElementById("formula").innerHTML = s.formula.main + "<small>" + s.formula.note + "</small>"; document.getElementById("mindmap").innerHTML = '<ul class="mindmap"><li class="root">' + s.mindmap.root + "</li>" + s.mindmap.items.map(i => "<li>" + i + "</li>").join("") + "</ul>"; document.getElementById("transcript").innerHTML = s.transcript.map(function(t){ if(Array.isArray(t)){ return '<span class="tag ' + t[0] + '">' + t[1] + "</span>"; } return t + "<br>"; }).join(""); document.getElementById("dialogue").innerHTML = (s.dialogue || []).map(function(m){ return '<div class="msg ' + m.r + '">' + m.t + "</div>"; }).join(""); document.getElementById("step").textContent = (cursor + 1) + "/" + history.length; document.getElementById("btnBack").disabled = cursor <= 0; document.getElementById("btnFwd").disabled = cursor >= history.length - 1; renderTimeline(); } ``` Untrusted JSON can reach these sinks through file import: ```javascript function importJSON(file){ if(!file) return; const reader = new FileReader(); reader.onload = function(e){ try{ const data = JSON.parse(e.target.result); const arr = Array.isArray(data) ? data : (data.history || []); if(!arr.length) throw new Error("No history data"); history = arr; cursor = 0; paint(); }catch(err){ showToast("Import failed", err.message); } }; reader.readAsText(file); ...[truncated 3100 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `innerHTML` with safe DOM operations for textual content: - Assign text through `textContent`. - Create lists, dialogue bubbles, transcript entries, and timeline nodes with `document.createElement()`. - Apply classes only after validating values against strict allowlists. 2. Validate all state objects before storing or rendering them: - Require the documented top-level fields. - Enforce expected scalar, array, and object types. - Restrict `dialogue.r` to `t` or `s`. - Restrict board types to an explicit allowlist. - Reject unknown properties, excessive nesting, and oversized strings. 3. Do not permit unrestricted HTML: - Prefer a structured visualization format rendered by application-owned code. - If HTML or SVG support is essential, process it with a maintained, strict allowlist sanitizer. - Remove scripts, event-handler attributes, unsafe URL schemes, embedded documents, `foreignObject`, and other active content. - Apply SVG-specific sanitization rather than treating SVG as ordinary HTML. 4. Add a restrictive Content Security Policy: - Avoid inline event handlers and inline scripts. - Do not enable `unsafe-inline`. - Restrict scripts, connections, images, frames, objects, and base URLs to required sources. 5. Treat AI-generated markup and lesson-history files as untrusted: - Display a warning before importing externally obtained files. - Validate and sanitize every imported history entry. - Sanitize again when exporting or redistributing legacy state. 6. Add automated security tests using payloads in every rendered field to verify that imported and API-submitted content cannot execute JavaScript or inject active UI elements. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/smart-classroom-serve.py:35
Finding
Unauthenticated Wildcard-CORS Access to Local Classroom State API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smart-classroom-serve.py`, lines 35-55 and 87-98 **Vulnerability Type**: Missing authentication and unrestricted cross-origin API access **Risk Level**: Medium ### Vulnerable Code ```python def do_GET(self): path = self.path.split("?", 1)[0] if path in ("/", ""): path = "/" + _INDEX if path in ("/state.json", "/api/state"): return self._serve_state() return self._serve_static(path) def do_POST(self): path = self.path.split("?", 1)[0] if path != "/api/state": return self._send_json(404, {"error": "not found"}) try: length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) data = json.loads(body.decode("utf-8")) except Exception as exc: return self._send_json(400, {"error": "invalid JSON: %s" % exc}) with open(_STATE_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return self._send_json(200, {"ok": True, "state": data}) ``` All API responses and preflight requests receive permissive CORS headers: ```python def _send_json(self, status, obj): data = json.dumps(obj, ensure_ascii=False).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(data))) self.send_header("Cache-Control", "no-store") self._cors_headers() self.end_headers() self.wfile.write(data) def _cors_headers(self): self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Content-Type") ``` ### Technical Analysis The service binds to `127.0.0.1`, which prevents direct connections from remote hosts but does not establish a trustworthy authorization boundary. A webpage loaded from the Internet can still send requests f ...[truncated 2528 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require authorization: - Generate an unpredictable per-session token when the server starts. - Require the token for both state reads and writes. - Avoid placing reusable credentials in source code or exported lesson files. - Invalidate the token when the server stops. 2. Restrict cross-origin access: - Remove `Access-Control-Allow-Origin: *`. - Permit only an explicitly configured trusted origin when cross-origin access is genuinely required. - Validate the `Origin` header before returning sensitive data or accepting writes. - Return `Vary: Origin` when dynamically allowing trusted origins. - Reject untrusted preflight requests. 3. Apply request limits before reading the body: - Reject missing, negative, malformed, or excessive `Content-Length` values. - Set a small maximum suitable for classroom state. - Limit JSON nesting, array lengths, history size, and individual string lengths. 4. Validate the state against a strict schema: - Require known fields and expected types. - Reject arbitrary HTML or sanitize it before storage. - Reject unknown fields and invalid role or board-type values. 5. Improve file handling: - Write to a temporary file and atomically replace `state.json`. - Set restrictive file permissions where supported. - Handle malformed or partially written state without crashing request processing. - Avoid storing student identifiers or other sensitive data unless necessary. 6. Add browser-focused tests confirming that an unrelated origin cannot read or update the local API, including preflighted JSON requests and loopback-origin attack scenarios. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
声明描述的是一个高层、完整的智慧课堂 AI 能力系统,而给出的代码块仅是其本地工作台配套的基础服务组件。代码只监听 127.0.0.1 端口,处理 GET/POST/OPTIONS,请求静态 HTML/资产文件,并对 state.json 进行读写。虽然这可被视为教学工作台的支撑基础设施,但与声明中的核心能力相比差距很大:没有任何模型调用、音视频处理、教学分析、个性化策略、内容生成或安全审查逻辑。该代码的主要目的与声明的主要目的存在实质性不一致,因此应判定为描述与实际行为不匹配。

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill explicitly describes capabilities that involve reading reference files and, in some modes, writing state/output files, but it does not declare any tool scope or allowed-tools policy. This creates an authorization ambiguity where a host may grant broader filesystem access than necessary, increasing the risk of unintended file reads/writes if the skill is activated.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger list includes broad, common terms such as '备课', '演示', and '讲解', which can cause the skill to activate in many unrelated conversations. Unintended activation is risky here because the skill also claims file and state interactions, so normal user prompts may be routed into a more privileged workflow than intended.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The role-prefix instruction requires users to enter messages beginning with the Chinese labels “教师:” or “学生:”, and the document does not offer alternative locale or language forms. This creates a language-specific constraint without explicit user opt-in, which is a natural-language policy concern.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The workbench defines a generic scene explicitly labeled as supporting arbitrary non-teaching content and unrestricted SVG/HTML rendering. In a browser context, this broadens the trust boundary beyond the declared classroom purpose and creates a reusable rendering surface that can display active or misleading content unrelated to the skill’s intended domain.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
renderBoard returns b.content directly for both HTML and SVG, and paint assigns that output to innerHTML. If untrusted state reaches board.content, transcript, mindmap items, formula fields, or dialogue text, the page can render attacker-controlled markup, enabling DOM XSS, script execution via browser-supported vectors, UI redress, or hostile embedded content.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Imported JSON is accepted without validation and placed into history, after which multiple fields are rendered with innerHTML. A crafted JSON file can therefore inject active HTML/SVG payloads and trigger script-capable browser behaviors or deceptive UI when a user imports a seemingly harmless teaching replay file.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The skill explicitly processes student performance data and only mentions desensitization as a downstream constraint, without a prominent user-facing warning or data-minimization guidance before collection and analysis. In an education context, this can lead operators to ingest identifiable minors' data into analysis workflows without adequate notice, consent, or handling safeguards.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
L237 声称工作台“不回写任何文件”,且 L321-L322 进一步强调“纯只读显示层”“不回写 state.json”。但 L243-L246 同一文档又定义本地服务提供 `POST /api/state` 写入教学状态,L248 也说明 `state.json` 会在推送时生成。该文档对系统是否会写文件给出了相互冲突的意图描述。

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The document defines local HTTP endpoints that expose classroom state for read/write operations, but does not warn about risks such as unauthorized local-network access, accidental exposure, or sensitive classroom content being retrievable from the service. Even on localhost-by-default systems, operators may bind differently, proxy traffic, or run in shared environments, making teaching data accessible beyond the intended audience.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
Allowing direct SVG/HTML injection for rendering is dangerous because untrusted content can carry active script, event handlers, external resource loads, or browser-based payloads, leading to cross-site scripting or data exfiltration in the workbench context. In this skill, content may be generated from conversational input or external state updates, so the attack surface is materially increased.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The document is explicitly marked with lang="zh-CN" and all visible UI text is hard-coded in Chinese, with no indication that users can select another language or that the tool is intentionally limited to a China-specific deployment. This creates a natural-language locale policy concern because the skill appears to enforce a specific language experience without opt-in.

Context-Inappropriate Capability

Low
Confidence
76% confidence
Finding
The manifest describes a teaching capability layer coordinating with host models and peripherals, but this file silently polls `state.json` over HTTP every 1.2 seconds and commits any changed content into history. That external-state ingestion mechanism is an implementation capability not clearly justified or disclosed by the teaching-oriented description, especially since it can drive arbitrary rendered state.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
Line L32 uses a multilingual dialogue example that fixes the target language to English. Because the policy requires flagging language or locale constraints unless the user is offered a choice or the constraint is clearly justified, this natural-language example can be read as imposing a language preference without opt-in.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
This markdown file presents all instructions and examples exclusively in Chinese, and there is no statement that users may choose another language or that the skill is intentionally limited to a Chinese-language context. Under the policy rule for language or locale constraints, forcing a single language without opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The entire exemplar is written in Chinese and presents the skill structure solely in that language, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-only audience. Under the stated policy, a language or locale constraint should either be optional for the user or clearly justified.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The file’s instructional content is entirely in Chinese and does not indicate that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the constraint is explicitly documented and justified.

Intent-Code Divergence

Low
Confidence
84% confidence
Finding
文档在 L243-L248 明确把本地服务描述为包含 `POST /api/state` 的“读写接口”,用于外部推送并生成 `state.json`。但 L321-L322 又以整体“工作台”口径描述 `state.json` 为“轮询为单向只读 GET”,弱化甚至否认了同一集成方案中的写入能力,容易误导开发者对实际行为边界的理解。

Static analysis

No suspicious patterns detected.