Skip to content

Lab Page & Terminal UI ​

This page covers the student-facing lab experience: the scenario catalog, the orientation page, the lab page's phases, the first-run walkthrough and help aids, the two-pane layout, the exercise panel, the terminal, DVWA and the SIEM viewer.

Scenario catalog ​

The catalog is static data in the portal, cyberrange/portal/src/hooks/useScenarios.ts. Each entry has an internal id (used for routing, provisioning and scoring), a student-facing displayNumber, a name, offensive or defensive type, a description, a MITRE technique, a difficulty (1 to 3), a guide file, a list of milestones with points, goal-style instructions and side cues, and a labSurface (terminal, dvwa or siem) that picks the right-hand pane. Scenario 3 also has an unscored prepTask.

DisplayInternal IDNameTypeDifficultyPoints
Scenario 101Network Reconnaissance & ExploitationoffensiveEasy (1)275
Scenario 206SQL InjectionoffensiveMedium (2)350
Scenario 309SIEM Alert TriagedefensiveMedium (2)225
Scenario 411Vulnerability HardeningdefensiveHard (3)200

Difficulty labels and durations come from components/scenarios/DifficultyBadge.tsx (Easy 30 min, Medium 45 min, Hard 60 min). Since pull request 167 these durations are also the real lab time limits enforced by the API.

Scenario 0: orientation ​

/scenario/orientation (app/scenario/orientation/page.tsx, linked from My Labs by OrientationCard) is a static page that explains the two-pane layout, automatic scoring, terminal prompts and flag submission, then links into Scenario 1. It starts no lab, is not in the SCENARIOS array and has no score.

It is not the same as the first-sign-in /orientation page (app/orientation/page.tsx), which OnboardingGate shows once per account (student and instructor versions; admins skip it) and which records completion with POST /users/me/orientation (onboarding_router.py).

My Labs cards ​

components/scenarios/ScenarioCard.tsx shows the scenario number, a Recommended next badge (lib/scenarioProgress.ts), difficulty, duration and role, description, progress ("Completed · N pts", "In progress" with a progress bar, or "Not started · N pts to earn") and a button: Start lab, Continue lab or Review lab.

Lab page phases ​

app/scenario/[id]/page.tsx is driven by hooks/useScenarioPod.ts, which polls the lab status: every 3 seconds while provisioning or destroying, every 10 seconds in the last 15 minutes of a session, and every 60 seconds otherwise.

PhaseComponentWhat the student sees
idleScenarioInfoViewObjectives, milestones, points earned, a tip card (ScenarioTips), a "Before you start — what this lab assumes" box (PrereqBox), Start lab, reset offer when fully completed
provisioningProvisioningViewChecklist: "Creating your machines", "Connecting the network", "Linking the SIEM", "Opening your terminal"; an elapsed timer; "Usually takes 30 to 60 seconds"; a slow-start note after 75 seconds; a fixed quote per scenario (CyberQuote)
activetwo-pane lab shellExercise panel on the left; terminal, DVWA pane or terminal plus SIEM on the right; Score with Time left above
expiredSessionExpiredOverlay (or the DVWA pane's own shutdown overlay)"Your 30-minute lab time limit ended…", using the scenario's own limit
failedlab pageError with a retry

The Big Picture modal (BigPictureModal) opens automatically the first time a scenario is visited (remembered in the browser by lib/bigPictureSeen.ts) and is available from a button afterwards.

The prerequisites box (components/scenario/PrereqBox.tsx, data in lib/scenarioPrereqs.ts, pull request 234) lists, per scenario, the tools and commands the tasks use and the concepts worth knowing, one plain sentence each. It starts expanded and can be collapsed.

First-run walkthrough and help ​

Since pull requests 254, 255, 263 and 238 the active lab page has three help aids:

  • Lab walkthrough (components/scenario/LabTour.tsx): four coach marks anchored to data-tour hooks: the current task panel, the lab pane (for DVWA: "DVWA is already signed in"; otherwise "Your console is already connected"), the task Status area ("Watch for the ✓") and the help buttons. It starts automatically about 600 ms after the student's first lab becomes active, once per user in that browser (lib/labTourSeen.ts, a localStorage key per user, so a shared lab PC still shows it to each new student). The ? button in the lab header replays it from step 1.
  • Troubleshooting drawer (components/scenario/TroubleshootingDrawer.tsx): a slide-over with fixes filtered by lab surface, for example "The terminal stopped responding" (reload; the lab keeps running), "DVWA sent me back to the login page" (select Home), a task stuck on Pending, and a warning that Reset score permanently deletes that scenario's points.
  • Start here (ExercisePanel.tsx): on a fresh attempt (lab active, nothing scored, first task selected) the exercise panel is outlined and shows a Start here badge with a one-line explanation.

Two-pane lab layout ​

Pull requests 156 to 164 replaced the single terminal page with a two-pane shell (app/scenario/[id]/page.tsx): the exercise panel on the left and the lab surface on the right. Since pull request 181 the two panes share the width equally (min-[900px]:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]; previously a fixed 380 px left pane). Below 900 px wide the panes stack. LabSurface.tsx picks the right-hand pane from getLabSurface(scenario):

ScenarioSurfaceRight-hand pane
1terminalTerminalView with the Kali tab only, labelled Host:
2dvwaDvwaView, the embedded DVWA browser (see below)
3siemTerminalView with Kali and a visually distinct meta tab; Open SIEM opens the alert viewer in a modal
4terminalTerminalView with meta open by default, a Scored badge and a status line; Kali is marked "optional playground, not scored"

Exercise panel ​

ExercisePanel.tsx holds everything about the current task:

  • Task outline (ScenarioOutline.tsx): a stepper with previous/next arrows and a dropdown of all tasks showing done, active, locked and points. A task unlocks only when every task before it is done; locked tasks show a lock icon and cannot be selected, and Next stays disabled until the current task is done. Completed tasks stay open for review. Gating is UI-only; the default active task is always the first unfinished one, so no one is trapped. Scenario 3's outline starts at Task 0 (preparation).
  • Goal and steps: the task's goal-style instructions from the catalog. Exact commands and payloads live once, in the walkthrough.
  • Status (TaskScoreStatus.tsx): a pending notice that the task scores automatically (with scenario-specific wording: DVWA's reply for Scenario 2, meta's state for Scenario 4), a success badge when scored, or, on flag tasks, the FlagSubmission box.
  • Manual Check: shown under "Not detected yet?". The panel says the student gets one check per task. After a failed check the button is replaced with "Your Manual Check has been used. An instructor needs to review…" (manual_check_locked from GET /pods/{id}/milestones). Ask an instructor to check this task opens the review form. Scenario 3 (SIEM) tasks have no Manual Check button, but the Ask an instructor to check this task link is shown on its unfinished scored tasks.
  • Step complete banner: when the scorer passes a task during the visit and the panel moves on, a dismissible "✓ Step complete: … scored +N pts. Next up: …" banner confirms it (lastScoredId from useScenarioMilestones). The panel's scroll position resets to the top when the task changes.
  • Progressive hints (components/scenario/ProgressiveHints.tsx, data in lib/scenarioHints.ts, pull request 236): under "Stuck? Reveal a hint", each task's hints are revealed one at a time: a nudge first, then the Exact step (a command, payload or UI action), with $TARGET_* placeholders replaced by the lab's real addresses. Revealing a hint is local to the page; it is not recorded and costs no points.
  • Walkthrough (GuideView.tsx): collapsible (labelled "Walkthrough for this task" on every scenario), and shows only the current task's ### Task N section of the guide (extractTaskSection() in lib/guideSections.ts, fence-aware; it falls back to the full guide when a guide has no task headings). Since pull request 202 it has no inner height limit, so the panel's own scroll area is the only scrollbar. It stays mounted so the PDF export still prints the whole guide.

Milestone polling ​

hooks/useScenarioMilestones.ts is the single 3-second poller of GET /pods/{id}/milestones for a lab. It guards against stale results when the student switches labs or scenarios, shows a "Task auto-detected! +N pts" toast for a newly passed task (the first poll of a visit only records which tasks were already passed, so earlier passes are shown but not announced again), and drives the completion dialog when every task has passed (lib/scenarioCompletion.ts). Scenario 3's unscored preparation task is excluded from polling and completion.

Side cues ​

TerminalSideCue.tsx shows a short hint for the current task. Over the terminal it floats at the bottom-right of the right-hand pane; in the DVWA pane it sits inline under the module strip (placement="inline", pull request 223) so it never covers DVWA's own buttons. Cues come from the catalog (cue, cueVariant); info is the default and warning (amber, alert icon) is used for traps such as "DVWA Security must read Low (the lab sets it for you)" (Scenario 2 Task 1) and "No output from the grep check means the default password is gone" (Scenario 4 Task 2). Cues render inline code and bold.

Terminal ​

  • XtermView.tsx builds the URL ws(s)://<current host>/api/ssh-websocket?token=<access token>&pod_id=<id>&pod_type=<kali|meta|dvwa>.
  • terminalSessionManager.ts keeps sessions in a map keyed by lab and tab, so switching tabs does not reconnect, and sends {"type":"resize","cols":...,"rows":...} when the window changes size. Since pull request 212 the bridge applies that size to the lab's tmux session (clamped to 5 to 200 rows and 20 to 500 columns, with bursts coalesced), so full-screen programs fit the pane instead of staying at 80×24 (cyberrange/bridge-src/bridge.py).
  • Reconnect (pull request 224, XtermView.tsx): when the WebSocket closes, the terminal keeps its scrollback and shows an amber banner, "Terminal connection lost. Click Reconnect or refresh the page first…", with the bridge's last Error: line if there was one and a Reconnect button. It also retries automatically after 1, 2 and 4 seconds, except when the bridge closed with code 1008 or 1011 (authentication or lab failure). The retry counter resets only after a reconnected session has stayed authenticated for 5 seconds, so a session that drops immediately cannot loop.
  • Tabs depend on the scenario (tabsForScenario() in TerminalView.tsx): Scenario 1 is Kali only, Scenario 2 has Kali and dvwa, Scenarios 3 and 4 have Kali and meta. Students cannot run a task's commands on a host the scenario does not use.
  • The header shows the active host's IP with a copy button and a Tools reference; Scenario 3 also has Open SIEM.
  • Copy and paste: terminalSessionManager.ts handles Ctrl/Cmd+Shift+C, Ctrl+C with a selection, and Ctrl/Cmd+V itself, and focusSession() puts keyboard focus back in the terminal when a modal such as the SIEM closes. Before pull request 141, closing an overlay left focus outside xterm and broke copy/paste.

The server side is described in Terminal Access.

Score ​

Above the panes the lab page shows earned points out of the scenario total and Time left (LabCountdown, counting down to the scenario's own limit). The guide is rendered by MarkdownView with styled callouts (guideCallouts.ts); print styles keep callouts and code blocks intact in the PDF.

DVWA pane (Scenario 2) ​

DvwaView.tsx replaces the old Open DVWA link with a browser pane: title bar, Target: IP, a Required: Security Low badge, Open in new tab, and a strip with only Home, SQL Injection, XSS (Reflected), DVWA Security and Login. Home, Security and Login load in the frame. SQL Injection and XSS (Reflected) open in a new top-level tab, with an explanatory card left in the pane, because the proxy sandboxes those pages and only a top-level page keeps DVWA's SameSite=Lax cookie on form submits, which browser scoring needs. When the lab expires the frame is unmounted behind a shutdown overlay.

Changes since pull request 223:

  • Automatic sign-in (pull request 256, lib/dvwaAutoLogin.ts): when a GET for any page other than login, logout or setup is redirected to DVWA's login form, the proxy signs in to the student's own lab DVWA with DVWA's stock lab account, pins the security=low cookie, replays the request and sets the new session cookies under /lab/dvwa/. If any step fails, DVWA's own login page is served as before. The DVWA links in the terminal view now open DVWA's Home page rather than login.php.

  • Responsive layout (pull requests 225 and 245): the proxy injects /dvwa-theme/css/embedded.css before </head> of every proxied DVWA HTML page (injectDvwaEmbeddedStyles() in lib/dvwaProxy.ts) so DVWA, including its login page, fits a narrow pane.

  • Visible scoring failures (pull request 232): the proxy now waits (up to 5 seconds) for POST /internal/browser-score. If the API rejects it or does not answer, a banner is injected at the top of the DVWA page, for example "Your attack worked, but your active lab is a different scenario" (HTTP 409), "your lab is not active", or "the scoring service did not respond in time, so the score may not have updated" (browserScoreFailureHint(), injectScoreNotice() in lib/dvwaScoreSignals.ts).

  • Near-miss diagnostics (pull request 267): when an attempt clearly aimed at one milestone does not score, the proxy sends only an allowlisted reason code (for example SECURITY_NOT_LOW or UNION_NO_HASH) to POST /internal/browser-diagnostic for the instructor view; it is stored only when Scenario 2 diagnostics are switched on. See Lifecycle & Scoring.

  • GET /pods/{id}/lab-urls returns the lab's DVWA address and a live probe result.

  • Every DVWA page is served through the portal at /lab/dvwa/... (app/lab/dvwa/[[...path]]/route.ts), which:

    • requires a portal session;
    • forwards to the lab's DVWA proxy port (18300 + pod_id on 10.115.77.1);
    • allows only login and logout, security level settings, static assets, SQL Injection (/vulnerabilities/sqli) and Reflected XSS (/vulnerabilities/xss_r), and removes other DVWA menu entries (isAllowedDvwaLabPath, stripDisallowedDvwaMenu in lib/dvwaProxy.ts);
    • rewrites HTML, cookies and redirects to stay under /lab/dvwa, and sets a Content Security Policy;
    • inspects each response for successful attacks (lib/dvwaScoreSignals.ts) and reports them to the API. See Scenario 2.

Open SIEM (Scenario 3) ​

Open SIEM opens an accessible modal (Esc, backdrop or button to close; focus trap) containing SiemAlertViewer, which lists the lab's recent Wazuh alerts from GET /pods/{id}/alerts, refreshing every 15 seconds and pausing while the browser tab is hidden (components/scenario/SiemAlertViewer.tsx, siemAlertQuery.ts). Only six fields are shown: time, agent ID and name, rule ID, description and level. There is no Wazuh dashboard in this deployment; this viewer is the students' SIEM.

Since pull requests 187 and 201, event times are shown in UTC as YYYY-MM-DDTHH:MM:SSZ (formatAlertTimestampUtc() in siemAlertQuery.ts), the same form the Scenario 3 checks accept, and clicking an individual event copies <UTC timestamp> rule <id>. The status line marks the Updated clock as "your local time" and states that event times are UTC. A filter bar (AlertFilterBar.tsx) filters by rule, agent, severity and free text (matched against rule ID, description and agent name). When the manager is unreachable the viewer says templates on meta do not score unless every PASTE… value is replaced with real evidence.

Asking an instructor ​

VerificationRequestModal creates a review with an answer to "What command did you use?" and up to 5 optional screenshots. Large images are shrunk in the browser before upload so they fit the 6 MB proxy limit (lib/imageCompress.ts, lib/screenshotUpload.ts); the server re-validates and re-encodes every image. Requests and instructor feedback appear under My Verification Requests on the dashboard.

Ending a session ​

The student ends the session from the lab page. The portal calls DELETE /api/pods/{id}/destroy and shows destroy progress until the lab is gone (components/provisioning/DestroyPodConfirmation.tsx, PodDestroyingProgress.tsx).

Sources

cyberrange/portal/src/hooks/useScenarios.ts, hooks/useScenarioPod.ts, hooks/useScenarioMilestones.ts, app/scenario/[id]/page.tsx, app/scenario/orientation/page.tsx, app/orientation/page.tsx, lib/guideSections.ts, lib/scenarioPrereqs.ts, lib/scenarioHints.ts, lib/labTourSeen.ts, components/scenarios/*, components/scenario/* (ExercisePanel, ScenarioOutline, TaskScoreStatus, FlagSubmission, TerminalSideCue, LabSurface, DvwaView, TerminalView, GuideView, PrereqBox, ProgressiveHints, LabTour, TroubleshootingDrawer, SiemAlertViewer, AlertFilterBar), components/terminal/*, lib/dvwaProxy.ts, lib/dvwaAutoLogin.ts, lib/dvwaScoreSignals.ts, app/lab/dvwa/[[...path]]/route.ts, cyberrange/bridge-src/bridge.py.

Cyber Range capstone documentation (Capstone 2 · Group 2 · A4102). Documents main at commit 6971373.