T09 · Insecure Skill Coding Practices
- Location
assets/smart-classroom-workbench.html:438- Finding
Unauthenticated Cross-Origin State Injection Enables Browser-Side Code Execution
- Content
View full analysis
Vulnerability Details
File Location:
scripts/smart-classroom-serve.py:42-56, 97-101;assets/smart-classroom-workbench.html:438-451, 480-492, 576-583
Vulnerability Type: Unauthenticated cross-origin API access and stored HTML injection
Risk Level: HighTechnical Analysis
The localhost HTTP service accepts arbitrary JSON at
POST /api/statewithout authentication, authorization, origin validation, schema validation, or request-size restrictions:python 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})The service explicitly permits requests from every web origin:
python 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")Although the server binds only to
127.0.0.1, a malicious website opened in the same browser can issue cross-origin requests to the loopback service. The permissive preflight response allows that website to submit anapplication/jsonrequest to/api/state.The workbench automatically polls the resulting state every 1.2 seconds and commits it to the rendering history:
javascript function poll(){ fetch("state.json?t=" + Date.now(), {cache:"no-store"}) .then(r => { if(!r.ok) throw 0; return r.json(); }) .then(function(d){ const sig = JSON.stringify(d); if(sig === _lastPollSig) return; ...[truncated 5087 chars]- Remediation
View remediation
Remediation Suggestions
-
Authenticate state API requests
- Generate a cryptographically random token when the local server starts.
- Require the token in a custom request header or an authorization header for
GET /api/stateandPOST /api/state. - Do not place a reusable token in a publicly readable state file.
-
Restrict cross-origin access
- Remove
Access-Control-Allow-Origin: *. - If cross-origin access is genuinely required, use a narrow allowlist of expected origins.
- Validate the
Originheader and reject absent or unexpected origins for browser-facing state changes. - Do not enable unnecessary CORS access for state-reading endpoints.
- Remove
-
Validate the state schema
- Require the expected object structure and primitive types.
- Allow only known values for
board.typeanddialogue.r. - Reject unexpected properties where practical.
- Apply strict limits to request size, field lengths, history length, and collection sizes.
- Validate imported replay JSON using the same schema before assigning it to
history.
-
Eliminate unsafe HTML construction
- Render topic, formulas, notes, mind-map items, transcript entries, and dialogue text with
textContent. - Build lists, labels, and dialogue elements with
document.createElementrather than string concatenation. - Validate role values before applying them as CSS classes.
- Render topic, formulas, notes, mind-map items, transcript entries, and dialogue text with
-
Remove arbitrary active-markup support where possible
- Prefer structured visualization primitives or a fixed drawing schema instead of accepting arbitrary HTML.
- For SVG, parse and allow only necessary graphical elements and safe attributes.
-
Sanitize content when HTML or SVG support is essential
- Use a robust allowlist sanitizer.
- Remove scripts, inline event handlers,
javascript:URLs, active embedded content, foreign objects, external resource references, and unsafe SVG elements or attributes. - Apply sanitization to both API-supplied ...[truncated 707 chars]
-
