Skip to content

Enhance VulnerableApp Legacy UI to have toggle feature as well as Card generation logic#652

Open
luks-santos wants to merge 4 commits into
SasanLabs:masterfrom
luks-santos:feat/legacy-ui-challenge-toggle-cards
Open

Enhance VulnerableApp Legacy UI to have toggle feature as well as Card generation logic#652
luks-santos wants to merge 4 commits into
SasanLabs:masterfrom
luks-santos:feat/legacy-ui-challenge-toggle-cards

Conversation

@luks-santos
Copy link
Copy Markdown
Contributor

@luks-santos luks-santos commented Jun 7, 2026

This PR introduces a Scanner / Challenge mode toggle to the legacy UI, along with the challenge cards experience for practicing vulnerabilities. The implementation reuses and extends the existing master-detail logic of the legacy UI as much as possible and follows closely the design and behavior described in #570.

What was implemented

  • Mode toggle (Scanner / Challenge) in the navbar, allowing the user to switch between the existing Scanner view and the new Challenge view.

  • Challenge cards rendered per vulnerability level, each containing:

    • The challenge objective (ChallengeText).
  • Progressive hint reveal hints are shown one at a time via a "Reveal hint" button.

  • Gated payload the "Show Payload" button only unlocks after all hints have been revealed.

Notes

  • The logic was built on top of the existing legacy UI patterns (master-detail, vanilla JS, AJAX), trying to stay as faithful as possible to what was outlined in #570.

  • A mock fixture (mockAllEndPointJson.json) was added to support local testing of the toggle and challenge cards. It is not referenced by the HTML, the app still loads the real allEndPointJson endpoint.

Closes #571

Screenshots

image image image

Summary by CodeRabbit

  • New Features
    • Added a mode toggle in the navigation bar to switch between "Scanner" and "Challenge" learning modes.
    • Introduced interactive challenge cards with ordered hints, staged reveal of payloads, objectives, and controls that disable when challenges are unavailable.
    • Added sample vulnerability content including paired secure/unsafe levels so scanner mode falls back when challenge content is absent.
    • Responsive layout improvements for challenge cards across screen sizes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 7, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 732fce6e-9e75-4f9f-806e-df8d42b8f4d0

📥 Commits

Reviewing files that changed from the base of the PR and between ca6ec08 and e4e3f74.

📒 Files selected for processing (1)
  • src/main/resources/static/vulnerableApp.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/resources/static/vulnerableApp.js

📝 Walkthrough

Walkthrough

The PR adds a Scanner/Challenge mode toggle to the UI, mock vulnerability data with challenge cards, CSS for mode/tile styling, and JS to compute, render, and switch between scanner help and interactive challenge cards when levels are selected.

Changes

Challenge Mode and Scanner/Challenge Toggle

Layer / File(s) Summary
UI Structure and Layout
src/main/resources/static/index.html
A mode toggle control is added to the navbar with two buttons (Challenge disabled by default, Scanner active), and the help section is restructured to include a separate adjacent challengeCards container for challenge mode content.
Mode Constants and Global State
src/main/resources/static/vulnerableApp.js
Mode constants MODE_SCANNER and MODE_CHALLENGE are defined, and a global appMode variable is initialized to scanner mode.
Challenge Cards and Mode Toggle Styling
src/main/resources/static/vulnerableApp.css
CSS classes are added for mode-toggle buttons (active/disabled states) and challenge cards (grid layout, card structure, hint headers, reveal buttons, and payload value formatting). A responsive media query changes the grid to 2 columns at min-width 786px.
Challenge Cards Mock Data
src/main/resources/static/mockAllEndPointJson.json
A mock AuthenticationVulnerability endpoint is created with two levels: LEVEL_1 (Unsecure variant with two challenge card steps) and LEVEL_8 (Secure variant with empty challenge cards), including request metadata and curl attack payloads.
Level Selection and Mode Toggle Integration
src/main/resources/static/vulnerableApp.js
Challenge cards are computed when a level is selected, toggle availability is updated, mode-aware detail rendering is triggered, and mode toggle event listeners are registered during app initialization.
Challenge Cards and Mode Logic Functions
src/main/resources/static/vulnerableApp.js
Functions are added to check challenge availability, build single and full challenge card DOM elements (with ordered hints and payload reveal controls), extract challenge cards for a level, render mode-aware detail views, set the app mode, update toggle availability, and register mode toggle event listeners.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #571 — Enhance VulnerableApp Legacy UI to have toggle feature as well as Card generation logic

Poem

🐰 A scanner sleeps while challenges wake,
Two modes in one, a choice to make,
Cards with hints and payloads bright,
Reveal each step and learn by sight,
Click the toggle — hop into delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly summarizes the main changes: adding a toggle feature and card generation logic to the VulnerableApp Legacy UI.
Linked Issues check ✅ Passed All objectives from #571 are met: toggle feature implemented (Scanner/Challenge modes), card generation logic added, legacy UI patterns preserved (vanilla JS, AJAX, master-detail), and mock data fixture provided for testing.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue requirements: navbar toggle, challenge cards UI, hint reveal logic, and mode switching are all in-scope enhancements to the legacy UI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.69%. Comparing base (970e00f) to head (e4e3f74).

Additional details and impacted files
@@            Coverage Diff            @@
##             master     #652   +/-   ##
=========================================
  Coverage     54.69%   54.69%           
  Complexity      663      663           
=========================================
  Files            91       91           
  Lines          3587     3587           
  Branches        397      397           
=========================================
  Hits           1962     1962           
  Misses         1448     1448           
  Partials        177      177           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@luks-santos luks-santos marked this pull request as ready for review June 7, 2026 15:17
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/resources/static/vulnerableApp.js (3)

486-511: ⚡ Quick win

Consider caching challenge cards to avoid redundant lookups.

The function calls _getChallengeCardsForLevel every time it renders (lines 489-493). When called from the level selection flow, these cards were already retrieved at lines 57-61. While the duplicate lookup isn't critical, storing the current level's challenge cards in a module-level variable would eliminate this redundancy.

⚡ Optional: Cache challenge cards
+let currentLevelChallengeCards = [];
+
 function _callbackForInnerMasterOnClickEvent(
   vulnerableAppEndPointData,
   id,
   key,
   vulnerabilitySelected
 ) {
   return function () {
     if (currentId == id && currentKey == key) {
       return;
     }
     currentId = id;
     currentKey = key;
     clearSelectedInnerMaster();
     vulnerabilityLevelSelected =
       vulnerableAppEndPointData[id]["Detailed Information"][key]["Level"];
     this.classList.add("active-item");
-    let levelChallengeCards = _getChallengeCardsForLevel(
+    currentLevelChallengeCards = _getChallengeCardsForLevel(
       vulnerableAppEndPointData,
       id,
       key
     );
-    _updateChallengeToggleAvailability(levelChallengeCards);
+    _updateChallengeToggleAvailability(currentLevelChallengeCards);
     _renderDetailMode(vulnerableAppEndPointData);
     // ... rest of function
   };
 }

 function _renderDetailMode(vulnerableAppEndPointData) {
   let challengeCardsContainer = document.getElementById("challengeCards");
   let scannerHelp = document.getElementById("scannerHelp");
-  let challengeCards = _getChallengeCardsForLevel(
-    vulnerableAppEndPointData,
-    currentId,
-    currentKey
-  );
+  let challengeCards = currentLevelChallengeCards;

   challengeCardsContainer.innerHTML = "";
   // ... rest of function
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/vulnerableApp.js` around lines 486 - 511,
_renderDetailMode calls _getChallengeCardsForLevel on every render; add a
module-level cache (e.g., currentChallengeCards with associated keys/currentId
and currentKey) and update it when the level selection flow first retrieves
cards, then have _renderDetailMode use the cachedChallengeCards if
currentId/currentKey match; if cache miss, call _getChallengeCardsForLevel,
store the result in the module-level cache, and proceed as before. Ensure the
cache is keyed by currentId/currentKey and reference the symbols
_renderDetailMode, _getChallengeCardsForLevel, currentId, currentKey, and
buildChallengeCards when making the replacement.

397-408: ⚡ Quick win

Prevent redundant payload rendering on repeated clicks.

The "Show Payload" button rebuilds the payload DOM on every click. This is wasteful and could confuse users if animations or state exist. Consider disabling the button after the first click or checking if content is already rendered.

⚡ Suggested fix: Disable button after first click
    showPayloadBtn.addEventListener("click", function () {
      payloadEl.innerHTML = "";
      let desc = document.createElement("p");
      desc.classList.add("challenge-card-payload-description");
      desc.textContent = payload["description"] || "";
      let value = document.createElement("code");
      value.classList.add("challenge-card-payload-value");
      value.textContent = payload["value"] || "";
      payloadEl.appendChild(desc);
      payloadEl.appendChild(value);
      payloadEl.classList.remove("hide-component");
+     showPayloadBtn.disabled = true;
    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/vulnerableApp.js` around lines 397 - 408, The click
handler for showPayloadBtn repeatedly rebuilds payloadEl every click; make it
idempotent by early-returning if payload is already shown (e.g., check
payloadEl.classList.contains("hide-component") or payloadEl.children.length) or
by disabling the button after the first render (set showPayloadBtn.disabled =
true and/or remove the event listener inside the handler). Update the handler
for showPayloadBtn to skip DOM creation when payloadEl is already visible and
only append the description/value once (use the existing payload variable and
payloadEl), thereby preventing redundant rendering and state/animation
duplication.

370-372: 💤 Low value

Consider making hint ordering more explicit.

The sort comparator uses || 0 as a fallback for missing order properties. While this works, it could be clearer about the intent.

💡 Optional: More explicit ordering
-  let hints = (card["hints"] || []).slice().sort(function (a, b) {
-    return (a["order"] || 0) - (b["order"] || 0);
-  });
+  let hints = (card["hints"] || []).slice().sort(function (a, b) {
+    let orderA = typeof a["order"] === "number" ? a["order"] : 0;
+    let orderB = typeof b["order"] === "number" ? b["order"] : 0;
+    return orderA - orderB;
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/vulnerableApp.js` around lines 370 - 372, The sort
comparator for hints is unclear because it uses `|| 0` to fallback when `order`
is missing; update the comparator used where `hints` is computed (the
slice().sort(...) on card["hints"]) to make ordering explicit: coerce `a.order`
and `b.order` to numeric values and use a clear default (for example with
nullish coalescing `??` or Number parsing) or extract a small helper like
`getOrder(item)` that returns a numeric order defaulting to 0, then sort by
`getOrder(a) - getOrder(b)` so missing/invalid orders are handled explicitly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/resources/static/vulnerableApp.js`:
- Line 194: The update() function repeatedly calls
_addingEventListenerToModeToggle which re-registers handlers on the same mode
toggle buttons; modify _addingEventListenerToModeToggle so it guards against
duplicate listeners by either (a) tracking a boolean like
modeToggleListenersAttached and returning early if true, or (b) removing
existing listeners before adding new ones (or use the { once: true } option
where appropriate); ensure the guard references the same DOM selectors used in
_addingEventListenerToModeToggle and set/clear the flag when toggles are
created/destroyed so listeners are only attached once per element.

---

Nitpick comments:
In `@src/main/resources/static/vulnerableApp.js`:
- Around line 486-511: _renderDetailMode calls _getChallengeCardsForLevel on
every render; add a module-level cache (e.g., currentChallengeCards with
associated keys/currentId and currentKey) and update it when the level selection
flow first retrieves cards, then have _renderDetailMode use the
cachedChallengeCards if currentId/currentKey match; if cache miss, call
_getChallengeCardsForLevel, store the result in the module-level cache, and
proceed as before. Ensure the cache is keyed by currentId/currentKey and
reference the symbols _renderDetailMode, _getChallengeCardsForLevel, currentId,
currentKey, and buildChallengeCards when making the replacement.
- Around line 397-408: The click handler for showPayloadBtn repeatedly rebuilds
payloadEl every click; make it idempotent by early-returning if payload is
already shown (e.g., check payloadEl.classList.contains("hide-component") or
payloadEl.children.length) or by disabling the button after the first render
(set showPayloadBtn.disabled = true and/or remove the event listener inside the
handler). Update the handler for showPayloadBtn to skip DOM creation when
payloadEl is already visible and only append the description/value once (use the
existing payload variable and payloadEl), thereby preventing redundant rendering
and state/animation duplication.
- Around line 370-372: The sort comparator for hints is unclear because it uses
`|| 0` to fallback when `order` is missing; update the comparator used where
`hints` is computed (the slice().sort(...) on card["hints"]) to make ordering
explicit: coerce `a.order` and `b.order` to numeric values and use a clear
default (for example with nullish coalescing `??` or Number parsing) or extract
a small helper like `getOrder(item)` that returns a numeric order defaulting to
0, then sort by `getOrder(a) - getOrder(b)` so missing/invalid orders are
handled explicitly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 44368e0c-2e5a-45b9-8410-81cf9b408730

📥 Commits

Reviewing files that changed from the base of the PR and between 970e00f and ca6ec08.

📒 Files selected for processing (4)
  • src/main/resources/static/index.html
  • src/main/resources/static/mockAllEndPointJson.json
  • src/main/resources/static/vulnerableApp.css
  • src/main/resources/static/vulnerableApp.js

});
});
_addingEventListenerToShowHideHelpButton(vulnerableAppEndPointData);
_addingEventListenerToModeToggle(vulnerableAppEndPointData);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Prevent event listener accumulation.

The function _addingEventListenerToModeToggle is called inside update(), which can be invoked multiple times (e.g., when switching vulnerabilities). Each invocation registers new listeners on the same mode toggle buttons without removing previous ones, causing memory leaks and multiple handler executions per click.

🔧 Recommended fix: Guard against duplicate listeners

Option 1: Use a flag to register listeners only once:

+let modeToggleListenersRegistered = false;
+
 function update(vulnerableAppEndPointData) {
   const masterItems = document.querySelectorAll(".master-item");
   handleElementAutoSelection(vulnerableAppEndPointData, 0);
   masterItems.forEach((item) => {
     item.addEventListener("click", function () {
       clearSelectedMaster();
       this.classList.add("active-item");
       detail.classList.remove("hidden-md-down");
       handleElementAutoSelection(vulnerableAppEndPointData, this.id);
     });
   });
   _addingEventListenerToShowHideHelpButton(vulnerableAppEndPointData);
-  _addingEventListenerToModeToggle(vulnerableAppEndPointData);
+  if (!modeToggleListenersRegistered) {
+    _addingEventListenerToModeToggle(vulnerableAppEndPointData);
+    modeToggleListenersRegistered = true;
+  }
 }

Option 2: Use { once: true } if listeners should only fire once, or move registration outside update() to a one-time initialization block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/static/vulnerableApp.js` at line 194, The update()
function repeatedly calls _addingEventListenerToModeToggle which re-registers
handlers on the same mode toggle buttons; modify
_addingEventListenerToModeToggle so it guards against duplicate listeners by
either (a) tracking a boolean like modeToggleListenersAttached and returning
early if true, or (b) removing existing listeners before adding new ones (or use
the { once: true } option where appropriate); ensure the guard references the
same DOM selectors used in _addingEventListenerToModeToggle and set/clear the
flag when toggles are created/destroyed so listeners are only attached once per
element.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance VulnerableApp Legacy UI to have toggle feature as well as Card generation logic

2 participants