Generate a new handler activation code. The plaintext is shown once -- save it before closing.
Full ruleset for EduScan v1.x. Run nexus scan to check the codebase against these rules.
| Code | Severity | Description | What It Catches | How to Fix |
|---|---|---|---|---|
HEUR-001 |
low | Missing title tag | HTML files with no <title> element |
Add a descriptive <title> inside <head> |
HEUR-002 |
low | Missing meta description | Files without <meta name="description"> |
Add a meta description tag in <head> |
HEUR-003 |
medium | Broken internal link | href/src attributes pointing to paths that do not exist on disk | Correct the path or remove the dead reference |
HEUR-004 |
low | Empty anchor | <a> tags with no text content or accessible label |
Add link text or aria-label |
HEUR-005 |
low | Image missing alt attribute | <img> tags with no alt |
Add descriptive alt text; use alt="" for decorative images |
HEUR-006 |
warning | Emoji detected | Unicode emoji characters in HTML content | Replace with webp icon from /assets/images/icons/ |
HEUR-007 |
medium | Console.log left in production | console.log() calls outside of dev/debug files |
Remove or replace with conditional debug logging |
HEUR-008 |
high | position:fixed under filter | Elements using position:fixed when a parent has filter applied — breaks layout |
Switch to position:absolute + scroll offset, or restructure DOM hierarchy |
HEUR-009 |
low | Hardcoded localhost URL | References to localhost or 127.0.0.1 in source files |
Replace with relative paths or config-driven base URLs |
HEUR-010 |
medium | Inline style overuse | Files with excessive inline style="" attributes that should be class-based |
Move repeated styles to a CSS class |
HEUR-011 |
low | TODO / FIXME comment | Source comments containing TODO, FIXME, HACK, or XXX markers | Resolve the issue and remove the marker, or create a sprint item |
HEUR-012 |
warning | Missing viewport meta | HTML files without <meta name="viewport"> |
Add the standard viewport meta tag to <head> |
HEUR-013 |
medium | Deprecated HTML element | Use of <font>, <center>, <marquee>, or similar deprecated tags |
Replace with CSS equivalents |
HEUR-014 |
high | innerHTML with unsanitized input | Patterns like element.innerHTML = userValue without escaping |
Use textContent or a sanitizer; escape all untrusted data |
HEUR-015 |
low | Large file warning | Single files exceeding the configured size threshold (default: 200KB) | Split into modules or lazy-load heavy sections |
HEUR-016 |
warning | Missing language attribute | <html> tags without a lang attribute |
Add lang="en" (or appropriate BCP 47 code) to the root element |
HEUR-017 |
high | Lazy-loaded platform component | Platform components (ModuleProgress, GameTracker, etc.) loaded via createElement('script') instead of a static <script src> tag. Bypasses DEP-004 checks, causes race conditions, and can fire completion before the student finishes reading. |
Replace with static <script src> and use a deliberate "Mark Complete" button instead of auto-scroll triggers |
HEUR-018 |
medium | Scroll-triggered auto-completion | Calls ModuleProgress.complete() inside a scroll event listener. Completion fires automatically at a scroll threshold (often 80%) without the student taking a deliberate action. Student has no feedback about what triggered it and may miss 20% of content. |
Replace with a "Mark Complete" button at the bottom of the page. The student clicks deliberately after reading all content. |
HEUR-019 |
medium | Tenant config missing required fields | Tenant dashboard file is missing slug, branding, licensing, or adminUids references required for multi-tenant operation. | Add all required tenant config fields per the tenant schema documentation. |
HEUR-020 |
medium | Tenant dashboard broken asset references | Tenant dashboard has absolute image/icon/CSS paths that do not resolve within the _app directory. | Fix asset paths to be relative or use the correct absolute path from the hosting root. |
HEUR-021 |
medium | Missing house content in tenant license | Tenant config licenses a house that has no content files in its directory. Tenant is paying for empty content. | Add content to the licensed house or remove it from the tenant license. |
HEUR-022 |
warning | Over-deep relative index.html link | An href with ../ climbs more parent directories than the file's depth inside its house, causing the link to escape the house directory entirely. |
Reduce the number of ../ segments to stay within the house boundary. |
HEUR-023 |
high | Broken Course Home link | An href to index.html resolves to a file path that does not exist on disk (archived or deleted). Student clicks "Course Home" and gets a 404. | Update href to point to a valid index.html (e.g., the course hub or house root). |
HEUR-024 |
warning | Missing Course Home link | Page loads ModuleProgress.js but has no <a href="...index.html"> link. The completion overlay's "Course Home" button will not appear. |
Add a navigation link (visible or in breadcrumbs) with href pointing to the parent index.html. |
HEUR-025 |
high | Module completion ID mismatch | The hub/index page expects a module ID (e.g., clh-001) but the module file saves completion under a different ID (e.g., script-clh-001-intro). Completions are saved but never read back — student sees no progress. |
Align IDs: either change the hub's module ID or change the ModuleProgress.complete() call to use the same key. |
HEUR-026 |
high | Course module links to house root instead of course hub | A file inside a course directory (e.g., courses/clh/modules/) has a back/home link that escapes to the house root instead of the course's own index.html. Student leaves the course context. |
Reduce the ../ depth to point to the course hub, not the house root. |
| Code | Severity | Description | What It Catches | How to Fix |
|---|---|---|---|---|
SEC-001 |
high | API key exposed in source | Hardcoded API key strings (non-Firebase) in HTML/JS files | Move to environment config; never commit secrets to source |
SEC-002 |
high | eval() usage | Calls to eval(), new Function(string), or setTimeout(string) |
Replace with explicit function references or safe dynamic dispatch |
SEC-003 |
high | document.write() usage | Calls to document.write() which can open XSS vectors |
Replace with DOM manipulation methods |
SEC-004 |
medium | External script without integrity | <script src="..."> loading from CDN without a integrity hash |
Add SRI integrity attribute or self-host the dependency |
| Code | Severity | Description | What It Catches | How to Fix |
|---|---|---|---|---|
HTML-001 |
medium | Unclosed HTML tag | Tags that are opened but never closed (detected via heuristic parse) | Close all opened elements; use an HTML validator to confirm |
JS-001 |
medium | Missing semicolons | JS statements that rely on ASI in ways that can produce bugs | Add explicit semicolons at statement ends |
PATH-001 |
medium | Absolute path in source | Hardcoded absolute filesystem paths (e.g. /home/user/...) |
Replace with relative paths or use Paths config helper |
| Code | Severity | Description | What It Catches | How to Fix |
|---|---|---|---|---|
LP-008 |
medium | Content type mismatch | LearningPaths module declares one type (e.g. applet) but the href resolves to a different file type |
Align the declared type with the actual file extension, or move the file to the correct directory |
| Code | Severity | Description | What It Catches | How to Fix |
|---|---|---|---|---|
QUIZ-001 |
high | Redundant client-side answers | Quiz has serverGrading: true but still contains correct: fields — answers exposed via View Source even though server has them |
Remove all correct: fields from questions. Server-side gradeQuiz CF has the answers in Firestore quiz_keys/ |
QUIZ-002 |
high | Client-side answers exposed | Quiz has correct: fields without serverGrading — answers fully visible to students via View Source |
Add serverGrading: true and houseId, seed answers to Firestore quiz_keys/, then remove correct: fields |
QUIZ-003 |
critical | Broken quiz — no answers anywhere | Quiz has NO serverGrading and NO correct: fields — grades everyone 0%. Students complete real work and get no credit |
Add serverGrading: true and houseId, seed answers to Firestore. This is a vulnerability chain: missing config + silent failure + recorded 0% = student impact |
QUIZ-004 |
critical | Quiz regression — server grading reverted | A quiz that was verified as server-graded in the baseline no longer has serverGrading: true or houseId. An edit (path fix, CSS, accessibility) accidentally reverted the server grading config |
Restore serverGrading: true and houseId. Check git log for what changed. Baseline: 230 verified quizzes in quiz-baseline.json |
Run these commands from the project root on the build machine. Reports are written to _tools/reports/ and the findings store is updated at _tools/nexus/findings.json.
--dry-run to preview without writing._tools/._tools/ is gitignored. Report files must be generated locally and are never deployed. The findings.json is the only durable store; it is not tracked in git by default and must be explicitly added if you want to commit a snapshot.
Select two user accounts to merge. The higher XP, combined completions, and primary email are kept.
Upload a CSV file with columns: email, displayName, house, classCode
Find accounts with no class membership and no activity in 30+ days.
Search for a user, then grant or revoke admin or handler privileges. Changes take effect on their next page load.
nexus full --publish to sync live data
Deleting a tenant is a soft-delete that marks it as deleted and makes the dashboard inaccessible. Tenant data is preserved for audit purposes but cannot be easily restored.
Visualize navigation structure for any course hub. Data generated by eduscan --tree --all