Base platform for interactive playgrounds in one React app. This is intentionally separate from a portfolio: it should feel like a small tool surface, opened in its own tab, with a clear path back to lucascoliveira.com.
- No backend, databases, ingestion APIs, analytics SDKs, or uploads.
- No PII collection. Inputs are capped; demo state is disposable.
- CSP: meta tag in
index.html, dev headers invite.config.ts, andpublic/_headersfor Netlify-style hosts. - Visible disclaimer in the shared shell: educational sandbox, controlled isolation, no remote collection.
A chat-style lab that compares:
- Insecure rendering (conceptually:
element.innerHTML/dangerouslySetInnerHTML) - Sanitized rendering (DOMPurify allowlist)
It uses preset-driven messages so learners can click around without writing payloads manually. The demo shows impacts locally (UI injection, auto-execution risk, token access risk) without becoming a public exploit surface.
- The user does not execute arbitrary JavaScript from free-form input.
- Preset-only behavior runs inside a sandboxed iframe (no
allow-same-origin) with a tight CSP; alerts/logs are captured for learning feedback. - No external requests, uploads, credential fields, or exfiltration paths are allowed.
- Presets live in
src/features/xss/data/payloadPresets.ts. - Each preset defines:
draft: what gets inserted into the composerinsecureHtml: what the insecure view renders inside the sandboxed iframeiframeScript(optional): demo-only logic foralert()/console.log()inside the iframeexplainer: educational copy tied to the preset- impact logic is mapped in
src/features/xss/lib/simulateXssImpact.ts
- React 19 + TypeScript + Vite
- Material UI v9 + Emotion
- React Router v7
cd lucas-playgrounds
npm install
npm run devOpen the URL Vite prints (default http://localhost:5173).
npm run build
npm run previewIf assets live under a subpath, set the base when building:
VITE_BASE_PATH=/your-repo-name/ npm run buildBrowserRouter uses import.meta.env.BASE_URL so client routes match the build base.
| Path | Description |
|---|---|
/ |
Catalog of playgrounds |
/xss |
XSS Chat Playground (insecure vs sanitized) |
/methodology |
How demos stay safe and how to extend the app |
- App shell (chrome):
src/components/shell/*+src/app/layout.tsx - Routing:
src/App.tsx(React Router) - Catalog:
src/app/page.tsx+src/playgrounds/registry.ts - Design foundation:
src/theme/*+ global styles - Pages:
src/app/<page>/page.tsx(thin route entry points) - Features (future):
src/features/<id>/*(UI, hooks, types)
- Registry: add a new entry to
src/playgrounds/registry.ts(id/title/description/tag/status). - Feature: create
src/features/<id>/with a single exported root component (ex:<IdPlayground />). - Page entry: add
src/app/<id>/page.tsxthat renders the feature root component. - Route: add a
<Route path="<id>" element={<... />} />insrc/App.tsx. - Nav (optional): add a chip to
src/components/shell/PlaygroundNav.tsxwhen the playground is ready to be accessible. - Catalog card: when shipping it, flip the registry
statusfromcoming_soonand wire the card to link to the route.
Any static host works (Vercel, Netlify, Cloudflare Pages, S3, etc.). Replicate the security headers from public/_headers or your host’s config. Prefer a dedicated subdomain (for example playgrounds.lucascoliveira.com) separate from the portfolio origin.
Educational use. Keep demos controlled; do not copy unsafe patterns into production systems.