BLA
BLA는 오픈소스 Blue Defense MCP와 CLI로, 소프트웨어 자산을 자동 AI 리버스 엔지니어링으로부터 보호합니다
A fully browser-based, WebAssembly-powered web exploitation training platform — no backend server required.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A fully browser-based, WebAssembly-powered web exploitation training platform — no backend server required.
Web eXploitation Laboratory (WXL) is a CTF-style web exploitation training platform. Every challenge runs entirely in the browser: WebAssembly simulates a realistic backend environment so the platform can be deployed and used without any server infrastructure.README 전문 보기 ↗A fully browser-based, WebAssembly-powered web exploitation training platform — no backend server required.
Web eXploitation Laboratory (WXL) is a CTF-style web exploitation training platform. Every challenge runs entirely in the browser: WebAssembly simulates a realistic backend environment so the platform can be deployed and used without any server infrastructure.
| Layer | Technology |
|---|---|
| Documentation framework | VitePress 2.0.0-alpha.16 |
| UI framework | Vue 3 3.5 + UnoCSS |
| State management | Pinia 3 |
| Python runtime | Pyodide 0.29 |
| PHP runtime | php-wasm |
| WASM modules | Rust 2021 + wasm-pack |
| Attack session tracking | IndexedDB (idb package) attack-session persistence |
| Package manager | pnpm 10.28 |
Node.js >= 22.6 — challenge:keygen, create:challenge, challenge:validate, and challenge:analyze run through node --experimental-strip-types, which is unavailable on earlier releases. The remaining TypeScript scripts go through the bundled tsx and have no such floor. CI builds on Node 24.
pnpm >= 10 (npm install -g pnpm)
Rust toolchain (install via rustup)
wasm-pack (cargo install wasm-pack) — not a package dependency; install it into your Rust toolchain
wasm-tools (cargo install wasm-tools --version 1.249.0 --locked, or pnpm wasm:tools) — pinned to the same version CI installs; required by the L2 stage of pnpm challenge:verify, which runs wasm-tools validate on the generated payload. pnpm challenge:keygen also uses it for its strip and mutate passes, but degrades to a warning for each when it is absent.
Chromium for Playwright — required before the first pnpm challenge:verify or pnpm test:smoke run. After pnpm install, install the browser binary once with:
pnpm exec playwright install chromium
# 1. Clone the project
git clone https://github.com/fhsh-tp/web-exploit-laboratory.git
cd web-exploit-laboratory
# 2. Install Node.js dependencies
pnpm install
# 3. Build the WASM modules and start the dev server
pnpm dev
The dev server starts at http://localhost:5173 by default.
| Command | Description |
|---|---|
pnpm dev | Build the WASM modules and start the dev server |
pnpm build | Build the WASM modules and emit the static site |
pnpm docs:dev | Start the VitePress dev server only (skips the WASM build) |
pnpm docs:build | Build the VitePress static site only |
pnpm docs:preview | Preview the built static site |
pnpm test | Run the TypeScript / JavaScript unit tests (Vitest). Bare, it stays in watch mode — use pnpm test --run for a single pass |
pnpm test:smoke | Run the Playwright smoke tests against the built site |
pnpm wasm:build | Build every Rust WASM module |
pnpm wasm:test | Run the Rust unit tests (cargo test) |
pnpm wasm:tools | Attempt to install wasm-tools into the Rust toolchain; it silences failures and always exits 0, so confirm with wasm-tools --version |
pnpm fork:init | Rewrite the project identity after forking (author, GitHub URLs, and optionally the VitePress base). Requires --author and --repo; the package name changes only with --name or --rebrand, and the SITE_BASE environment variable is never written by the script |
pnpm challenge:keygen | Generate the encrypted WASM module for every challenge |
pnpm create:challenge | Scaffold a new challenge from flags; --name <slug> is required and a bare run exits 1 with its usage line |
pnpm challenge:validate | Validate every challenge's frontmatter and file layout |
pnpm challenge:analyze | Report the content and configuration of a challenge |
pnpm challenge:retype | Mutate an existing challenge's backend / difficulty / tags / category |
pnpm challenge:verify |
Browser
├── VitePress site (Vue 3 + UnoCSS)
│ ├── Challenge pages (Markdown + YAML frontmatter)
│ └── IndexedDB (attack-session persistence + tool state)
├── Service Worker (platform/public/challenge-sw.js)
│ └── Intercepts HTTP requests and routes them to the matching WASM runtime
└── WASM runtimes
├── virtual-fs Encrypted virtual filesystem (Rust)
├── asgi-bridge Python ASGI/WSGI bridge layer (Rust)
├── python-bridge Pyodide integration (TypeScript)
└── php-bridge php-wasm integration (TypeScript)
The two Rust crates live under chall-wasm/ and are built by pnpm wasm:build.
Every challenge is a Markdown file with a YAML frontmatter block declaring its configuration:
---
title: Need to Know # required
layout: challenge # selects the challenge UI; without it the page renders as a plain docs page
backend: fastapi # required — flask | fastapi | php
app: app.py # required — path relative to the challenge's src/ root
difficulty: easy
category: web
packages: [] # extra Python packages to install via micropip
tools: [ browser, network, repeater, code ] # omit the field to get exactly these four;
# list them explicitly to narrow the set, and
# browser is added back even if you drop it
source_visible: false # true = white-box, false = black-box (default)
wasmModule: /challenge/door-is-open/runtime.wasm # produced by keygen
---
Challenge source files are picked up by scanning the challenge's src/ directory. The legacy fs: mapping is still accepted for compatibility, but the validator emits a deprecation warning — new challenges SHALL rely on the src/ scan instead.
See CONTRIBUTE.md for the branching strategy, PR workflow, and commit conventions.
Build output lives in .vitepress/dist/ as plain static files and can be deployed to any static hosting service.
# 1. Install dependencies
pnpm install
# 2. Full build (WASM + keygen + VitePress)
pnpm build
Run fork:init with its required flags:
pnpm fork:init --author "<Your Name>" --repo <owner>/<repo> --base <see below>
It rewrites package.json, swaps the upstream GitHub URLs for yours, sets or clears the VitePress base per --base, and installs a ready-to-use GitHub Pages workflow at .github/workflows/deploy.yml by copying scripts/templates/deploy.yml.template. This repository no longer carries a deploy workflow of its own — it publishes through Cloudflare Pages, described in the next section — so a fresh fork has nothing at that path and the copy goes through. If you have already customised your own workflow there, the script leaves it untouched and warns instead of overwriting it.
Choosing --base is the step that decides whether your assets load. VitePress bakes the base path into every asset URL:
https://<owner>.github.io/<repo>/) → --base /<repo>/. The flag writes the literal path into .vitepress/config.mts. The copied workflow deliberately sets no SITE_BASE environment variable, so this literal is the only thing that makes a sub-path site work.--base none, which removes the base declaration so VitePress serves from /.Omitting --base leaves the upstream env-conditional base: process.env.SITE_BASE ?? '/' in place. That form exists for this repository's own tooling, where SITE_BASE may be supplied by the build environment; in a fork nothing sets it, so the site builds at /. That is correct for a root deployment and wrong for a GitHub Pages project site, where every asset would then 404.
Note for local testing after
--base /<repo>/. The literal base applies to every build, including local ones.pnpm test:smokeand the L3 stage ofpnpm challenge:verifynavigate to root-relative paths, so they will 404 against a site built with a sub-path base. Run them with the base temporarily cleared, or point their navigation at the sub-path.
fork:init does not touch the short-link origin. Set SITE_ORIGIN in .vitepress/challenge/shortLink.ts to your own site's address; otherwise every short link your site copies points back at this one.
Short links work only on Cloudflare Pages. They are rules in
platform/public/_redirects, a file Cloudflare Pages reads and GitHub Pages ignores. On a GitHub Pages fork every page still works, but a short link printed on a handout leads to a 404.
Two settings for a GitHub Pages fork live in the GitHub UI rather than in the repository:
github-pages environment must allow deployments from v* tags, in addition to the main branch that authorises workflow_dispatch runs. Without that rule the tag-triggered deploy job is rejected.This is how the upstream site is published.
Create a new project on Cloudflare Pages and link the GitHub repository.
Set the build command:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal \
&& . "$HOME/.cargo/env" \
&& cargo install wasm-pack \
&& pnpm install \
&& pnpm build
Set the output directory to .vitepress/dist.
Add NODE_VERSION=24 to the environment variables.
If the site is served from a sub-path, add SITE_BASE with that path (leave it unset for a root-domain deployment).
Note: Cloudflare Pages does not ship a Rust toolchain by default.
wasm-packis a Rust binary, not a package dependency —pnpm installwill not provide it — so the build command above installs the minimal Rust toolchain along withwasm-packbefore building.wasm-toolsis not installed here.pnpm builddoes runpnpm challenge:keygen, which useswasm-toolsfor its strip and mutate passes, but keygen degrades to a warning for each when the tool is absent — so the build still succeeds, just without those passes. Addwasm-toolsto the install line (cargo install wasm-tools --version 1.249.0 --locked, the same version CI pins) if you want stripped and mutated payloads in production builds.
This project is licensed under the Educational Community License, Version 2.0 (ECL-2.0).
| Run the layered verify gate (L1 lint, L2 build, L3 Playwright e2e) on a challenge |
pnpm challenge:verify:blind | Run the L4 blind-solve sub-routine standalone (also reached via pnpm challenge:verify <slug> --blind) |
pnpm challenge:verify:cross | L4 multi-agent cross-check — runs the blind gate against claude,codex,gemini and aggregates verdicts |
pnpm prepare | Install the git hooks (simple-git-hooks); runs automatically after pnpm install |