Skip to content

Design doc for Challenge introduction

Karan Preet Singh Sasan edited this page Apr 19, 2026 · 3 revisions

VulnerableApp Challenge Mode Design Doc

Purpose

This document describes the planned transition from the current scanner-first experience in VulnerableApp to a dual-mode experience:

  • Scanner Mode: existing behavior, unchanged.
  • Challenge Mode: a guided learning mode that renders level cards with challenge text, hints, and an optional final payload.

This document is intended to be the shared implementation guide for contributors working across VulnerableApp, VulnerableApp-Facade, and the facade-schema contract.


Why this change

Today, VulnerableApp is primarily consumed in scanner mode. That works well for direct vulnerability interaction, but it does not provide a guided learning flow for users who want to solve each level in a structured way.

Challenge Mode adds that missing layer without removing the existing scanner experience.


Goals

  1. Keep Scanner Mode fully backward compatible.
  2. Add Challenge Mode as an optional layer.
  3. Render each level as one or more challenge cards.
  4. Support a contract-driven model so the UI can be generated from structured data.
  5. Ensure the existing VulnerableApp ↔ VulnerableApp-Facade relationship remains intact.
  6. Allow existing vulnerable applications to continue working even if they do not yet provide challenge metadata.

Non-goals

  • No scoring system in this phase.
  • No user progress tracking in this phase.
  • No gamification mechanics in this phase.
  • No breaking rewrite of the current vulnerability modules.
  • No requirement to migrate every module at once.

Current architecture

Today

  • VulnerableApp defines vulnerability modules and level behavior.
  • VulnerableApp-Facade reads structured metadata and renders the UI shell.
  • The UI is primarily scanner-oriented.

Problem

The current model is good for direct interaction, but it does not naturally support:

  • guided challenge cards,
  • progressive hints,
  • an explicit challenge/scan toggle,
  • or a clean contract for challenge-specific fields.

Target architecture

Dual-mode user experience

Scanner Mode

  • Existing behavior remains the default.
  • Existing pages and interactions continue to work.
  • Existing metadata continues to render as before.

Challenge Mode

  • The UI renders level cards.
  • Each card exposes a challenge entry point.
  • Hints are shown inside the card.
  • A final payload can be shown hidden or collapsible.
  • Users can move through challenges one by one.

Contract direction

The core design principle is:

Existing data stays valid; challenge data is additive.

This means the contract must support both:

  • legacy vulnerability metadata,
  • and new challenge-specific fields.

Contract evolution philosophy

  1. Do not break current consumers.
  2. Add new fields only where needed.
  3. Make challenge fields optional.
  4. Keep a clear fallback path when challenge fields are absent.

Proposed contract shape

The new contract should describe data as:

Challenge data

This is the new layer for Challenge Mode:

  • challenge text
  • hints
  • optional payload

Recommended JSON structure

Vulnerability definition

{
  "name": "AuthenticationVulnerability",
  "id": "AuthenticationVulnerability",
  "description": "...",
  "vulnerabilityTypes": [],

  "levels": [
    {
      "levelIdentifier": "LEVEL_1",
      "variant": "UNSECURE",

      "resourceInformation": {
        "htmlResource": {
          "resourceType": "HTML",
          "isAbsolute": false,
          "uri": "/VulnerableApp/templates/AuthenticationVulnerability/LEVEL_1/Auth.html"
        },
        "staticResources": [
          {
            "resourceType": "CSS",
            "isAbsolute": false,
            "uri": "/VulnerableApp/templates/AuthenticationVulnerability/LEVEL_1/Auth.css"
          },
          {
            "resourceType": "JAVASCRIPT",
            "isAbsolute": false,
            "uri": "/VulnerableApp/templates/AuthenticationVulnerability/LEVEL_1/Auth.js"
          }
        ]
      },

      "challengeCards": [
        {
          "challengeText": "Bypass the authentication mechanism and gain access to the admin panel.",

          "hints": [
            {
              "order": 1,
              "text": "Look at how the username is validated."
            },
            {
              "order": 2,
              "text": "Check what happens when authentication fails."
            }
          ],

          "payload": {
            "description": "Final exploit payload to bypass login.",
            "value": "' OR 1=1 --"
          }
        }
      ]
    }
  ]
}

Notes on the contract

  • challengeCards is optional.
  • payload is optional.
  • The UI must fall back to legacy rendering when challengeCards is not present.
  • A single payload may be associated with multiple hint cards.
  • Hint cards are a UI grouping mechanism, not necessarily a security boundary.

Annotation strategy

Current situation

VulnerableApp already uses an AttackVector annotation to describe attack guidance for vulnerability methods.

New direction

We should not force all challenge data into the existing annotation immediately. Instead, we should introduce a newer challenge annotation and let the facade resolve data in this order:

  1. Use new challenge-specific metadata if present.
  2. Otherwise fall back to the legacy AttackVector data.

Example intent

  • AttackVector remains the compatibility anchor.
  • A new annotation can provide richer challenge text and card-level hint structure.
  • The facade merges or falls back depending on what is available.

Backward compatibility rules

Backward compatibility is a hard requirement.

The system must continue to support:

  • old modules without challenge data,
  • old facade consumers,
  • existing scanner-mode behavior,
  • existing level resources and identifiers.

Compatibility rules

  1. If challenge data exists, Challenge Mode may render cards.
  2. If challenge data does not exist, Scanner Mode continues to behave exactly as today.
  3. Legacy modules must not fail because challenge fields are absent.
  4. The facade must tolerate mixed versions during migration.

UI behavior

Scanner Mode

  • Keep the current UI intact.
  • Do not require challenge cards.
  • Do not hide or break existing functionality.

Challenge Mode

  • Add a toggle control in the UI.
  • Render challenge cards dynamically.
  • Each card should contain:
    • challenge text,
    • hint sections,
    • optional payload.
  • Hints should be collapsed or progressively revealed.
  • The payload should be hidden by default unless the design intentionally exposes it.

Card model

Each challenge card should be minimal and focused.

Recommended card contents

  • challenge text
  • one or more hint groups
  • optional payload

Recommended card structure

{
  "challengeText": "Extract data from the vulnerable endpoint.",
  "hintCards": [
    {
      "hints": ["Look at the request parameters.", "Try changing the object identifier." ]
    }
  ],
  "payload": {
    "description": "..."
    "value": "..."
  }
}

This keeps the UI simple and gives contributors a consistent target.


Facade responsibilities

VulnerableApp-Facade becomes the main translation layer between source annotations and UI output.

Facade should:

  • read vulnerability metadata,
  • read level resources,
  • detect challenge-related metadata,
  • build a card-ready model for Challenge Mode,
  • preserve the existing scanner-mode contract,
  • hide version-specific differences from the UI.

Facade should not:

  • embed UI logic directly,
  • hardcode per-vulnerability rendering rules,
  • break legacy schema consumers.

Migration approach

We should proceed in phases.

Phase 1: Contract foundation

  • Define the challenge-aware contract.

Phase 2: Facade rendering

  • Update facade-schema to support the new fields.
  • Agree on fallback behavior.
  • Add card generation.
  • Add the scanner/challenge toggle handling.
  • Ensure Challenge Mode only activates when challenge data is available.

Phase 3: Legacy UI support

  • Add the same toggle and card logic to the legacy UI.
  • Preserve existing scanner behavior.

Phase 4: Module-by-module content updates

  • Update each vulnerability module to provide challenge metadata where appropriate.
  • Start with high-value modules and the most straightforward levels.

Phase 5: Design doc and stabilization

  • Keep this document updated as the contract evolves.
  • Use it as the implementation reference for contributors.

Child ticket breakdown

The parent issue is challenge mode. The child tickets naturally split into these workstreams:

Contract and schema

  • Create contract JSON schema design.
  • Update facade-schema to support the new fields.
  • Update facade-schema to accept challenge text and hints.
  • Update the facade package to consume the newer schema.
  • Add a design doc that ties everything together.

UI and facade behavior

  • Add card generation to VulnerableApp-Facade.
  • Add toggle support between scanner and challenge modes.
  • Add the same behavior to the legacy UI.

Vulnerability module updates

  • Authentication
  • Command Injection
  • JWT
  • Cryptography Failures
  • File Upload
  • Path Traversal
  • SQL Injection
  • XSS
  • XXE
  • Open Redirect
  • SSRF
  • IDOR
  • Clickjacking
  • LDAP Injection

These modules should be migrated carefully so each one can provide challenge data without breaking its current scanner behavior.


Contributor guidance

When contributing to this effort:

  1. Prefer additive changes over breaking changes.
  2. Keep the contract optional and backward compatible.
  3. Keep the facade as the only transformation layer.
  4. Keep the UI as a pure renderer.
  5. Update one module or one layer at a time.
  6. When in doubt, preserve current scanner-mode behavior.

Suggested implementation order

  1. Finalize the contract.
  2. Update facade-schema.
  3. Update VulnerableApp-Facade parsing and card generation.
  4. Add toggle behavior to the UI.
  5. Migrate a small number of vulnerabilities as examples.
  6. Roll out the rest module by module.