Ein schlanker, zweistufiger Console-Banner, der Endanwender vor Self-XSS-Betrug („Kopieren Sie dies in die Konsole, um …") warnt. Das Banner erscheint nur dann, wenn die Browser-DevTools tatsächlich geöffnet werden — die Seite bleibt ansonsten vollständig unbelastet (0 Netzwerk-Requests, kein Render-Blocking, kein Tracking).
Tonalität und Muster sind am bekannten Infomaniak-Beispiel angelehnt; die Brand-Umsetzung folgt der BAUER-GROUP-CI (Orange #FF8500, Warm-Gray-Skala, System-Font-Stack).
<script
async
src="https://widgets.professional-hosting.com/console-banner/v1/loader.min.js"
></script>Empfohlen im <head>, so früh wie möglich. async ist korrekt: Der Loader blockiert nichts und hat keine DOM-Abhängigkeit.
Mit Subresource-Integrity (Hash aus console-banner/v1/integrity.json):
<script
async
src="https://widgets.professional-hosting.com/console-banner/v1/loader.min.js"
integrity="sha384-…"
crossorigin="anonymous"
></script>Für React-19-Projekte gibt es das npm-Paket
@bauer-group/console-security-banner-react — komfortabel
einzubinden, lokal gebundlet (0 Requests, offline-tauglich) als Default oder
optional vom CDN:
npm i @bauer-group/console-security-banner-reactimport { ConsoleSecurityBanner } from '@bauer-group/console-security-banner-react';
// einmal nahe der App-Wurzel (Next.js App Router, Vite, CRA …)
<ConsoleSecurityBanner /> {/* local (Default), Sprache automatisch */}
<ConsoleSecurityBanner mode="cdn" /> {/* stattdessen vom CDN laden */}Komponente und Hook (useConsoleSecurityBanner), SSR-sicher, mit "use client"
und Typen. Details: packages/react/README.md. Das
Paket teilt sich den Core (src/core) mit dem CDN-Widget — eine Single Source of
Truth.
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ Stage 1 · loader.min.js │ │ Stage 2 · banner.min.js │
│ ~1.2 KB gzip · eager │ ──► │ ~8 KB gzip · lazy │
│ · DevTools-Detect (Viewport)│ │ · 22 Sprachen (auto) │
│ · Resize + Visibility │ │ · styled %c-Konsolen-Log │
│ · stiller Intervall-Check │ │ · einmal pro Tab │
│ · bei Treffer → Stage 2 │ │ │
└──────────────────────────────┘ └──────────────────────────────┘
- Stage 1 wird auf jeder Seite eingebunden, ist idempotent und macht keine Requests, solange die Konsole geschlossen bleibt.
- Stage 2 wird vom Loader dynamisch nachgezogen, sobald DevTools erkannt werden — erst dann fällt ein einziger HTTP-Request an.
Detektion standardmäßig über das Outer-/Inner-Viewport-Delta (docked DevTools) — ohne Konsolen-Ausgabe, die Konsole bleibt makellos. Für strenge Sicherheitsanforderungen erkennt eine optionale Getter-Tripwire (data-probe) zusätzlich undocked DevTools (loggt dann einmalig einen Probe-Wert).
src/ TypeScript-Source (CDN-Widget)
core/ geteilter, framework-agnostischer Core …
detect.ts DevTools-Erkennung (Viewport; Tripwire opt-in)
render.ts i18n + styled %c-Konsolen-Render
loader.ts Stage-1 IIFE (dünner Wrapper um core/detect)
banner.ts Stage-2 IIFE (dünner Wrapper um core/render)
i18n/
index.ts Locale-Registry + Fallback 'en'
locales/*.json 22 Sprachen × {tagline,title,body,assurance,cta}
packages/react/ npm-Paket @bauer-group/console-security-banner-react …
src/ Hook + Komponente (TS) — nutzt denselben src/core
tsup.config.ts Build → ESM + CJS + .d.ts (use client)
scripts/
build.mjs esbuild: src/*.ts → dist/v1 + SRI (CDN-Widget)
deploy/ provider-agnostische Deploy-Schicht (cloudflare/s3/bunny)
secrets/sync.mjs .env ↔ GitHub-Secrets
deploy/zones.json Deploy-Ziele (eine Zone: console-banner-cf)
test/ Vitest + jsdom (Loader, Banner, i18n) — TS
demo/index.html lokale Vorschau
docs/ Deployment-, Provider- & Secrets-Doku
dist/ Build-Artefakt (gitignored)
Alles in src/** und packages/react/src/** ist TypeScript; esbuild (CDN)
und tsup (npm) kompilieren nach JS. dist/ wird nie von Hand editiert.
npm install
npm run build # dist/v1/{loader,banner}{.js,.min.js,.map} + integrity.json
npm run test # Vitest (jsdom)
npm run lint # ESLint
npm run validate # format:check + lint + test + build (CI-Gate)npm run build
npx serve . # Repo-Root ausliefern …
# … dann http://localhost:3000/demo/ öffnen und DevTools (F12) öffnenPro Stufe erzeugt npm run build unter dist/v1/:
| Datei | Zweck |
|---|---|
<name>.js |
lesbarer Bundle (für Audit/Debug) |
<name>.min.js |
produktives, minifiziertes Artefakt |
<name>.min.js.map |
Source-Map |
integrity.json |
SHA-384-SRI-Digests beider *.min.js |
esbuild bündelt den ESM-Quellcode (Banner importiert die Locale-JSONs) in ein eigenständiges IIFE — keine Runtime-Imports, eine gecachte Datei.
Reihenfolge: (1) <html lang> → (2) navigator.languages[] → (3) Fallback Englisch. Verglichen wird das Primär-Subtag (de-AT → de) gegen die ausgelieferten Locales.
Unterstützt (22): ar cs de en es fr hi hu it ja ko nl pl pt ro ru sv th tr uk vi zh.
Sprache ändern/ergänzen: Strings in src/i18n/locales/<lang>.json bearbeiten bzw. neue Datei anlegen und in src/i18n/index.ts registrieren, dann npm run build. Alle Locales teilen denselben 5-Schlüssel-Vertrag (per Test erzwungen).
Auslieferung via Cloudflare R2 (Bucket bg-widgets, Prefix console-banner/), fronted von widgets.professional-hosting.com.
npm run deploy:dry # Vorschau: geplante PUTs + Purge, keine Netzwerk-Writes
npm run deploy # Upload + URL-Purge der geänderten DateienDer Bucket wird mit anderen Projekten geteilt. Jeder Deploy bleibt strikt auf unsere Dateien beschränkt:
- Manifest-Sync-Delete (
console-banner/.deploy-manifest.json) löscht nur Keys, die dieses Repo selbst geschrieben hat. - Purge nur per URL (
purgeEverything: false) — andere Projekte werden nie aus dem Edge-Cache entfernt.
Details: docs/deployment.md · docs/providers/cloudflare.md · docs/secrets.md.
| Workflow | Auslöser | Funktion |
|---|---|---|
release.yml |
Push main |
Validate (lint/test/build) → Semantic Release |
deploy-cdn.yml |
Push main / manuell |
Build → Matrix-Deploy je Zone → URL-Purge |
deploy-pages.yml |
Push main |
Live-Demo-Vorschau auf GitHub Pages |
ai-issue-summary.yml |
Issue/PR geöffnet | KI-Triage-Zusammenfassung |
teams-notifications.yml |
diverse Events | Teams-Benachrichtigungen |
Vor dem ersten Deploy: GitHub-Secrets R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, CF_PURGE_TOKEN, CF_ZONE_ID setzen (identisch zur Microsites-Zone). Lokal via .env + npm run secrets:push.
| Attribut | Zweck |
|---|---|
data-cdn |
Überschreibt die Basis-URL, von der Stage 2 nachgeladen wird. |
data-banner |
Überschreibt den kompletten Stage-2-Pfad (Vorrang vor data-cdn). |
data-probe |
Opt-in für strenge Sicherheit: erkennt zusätzlich undocked DevTools (loggt einmalig einen Probe-Wert). Default aus → Konsole bleibt makellos. |
<!-- nur bei besonders strengen Sicherheitsanforderungen -->
<script async src="…/console-banner/v1/loader.min.js" data-probe></script>Anwendungsfälle: Staging/Preview, QA-Umgebungen, lokale Tests (siehe demo/).
Bei strikter CSP widgets.professional-hosting.com für script-src freigeben:
Content-Security-Policy: script-src 'self' https://widgets.professional-hosting.com; …
Bei nonce/hash-Policies: Der Loader injiziert Stage 2 dynamisch; Browser propagieren den Nonce nicht automatisch an Kind-Skripte. Einfachste Lösung: die CDN-Domain domänenbasiert erlauben.
| Metrik | Wert |
|---|---|
| HTTP-Requests (Konsole geschlossen) | 0 |
| HTTP-Requests (Konsole offen) | 1 (Stage 2, ~8 KB gzip) |
| Stage-1-Transfer | ~1,2 KB gzip |
| Render-Blocking | keines (async) |
| Cookies / Tracking | keine |
| Laufzeit-Overhead | 1× passiver Resize-Listener + 1× setInterval (2 s, selbstterminierend) |
- Undocked-DevTools (separates Fenster): Die Viewport-Heuristik greift nicht und wird standardmäßig nicht erkannt — die Konsole bleibt dafür makellos. Für strenge Sicherheitsanforderungen per
data-probe(bzw.probeim React-Paket) die Getter-Tripwire zuschalten; sie loggt dann einmalig einen Probe-Wert. - Gezielt blockiert: Sicherheitsbewusste Entwickler können den Loader abschalten — akzeptiert, das Banner richtet sich an Endnutzer.
console.clear()nach Stage 2: Seiten-Code, derconsole.clear()aufruft, kann das Banner entfernen; Reload löst es erneut aus.- Firefox:
%c-Stile (Farbe/Weight/Padding/Background) werden ab FF ≥ 109 korrekt gerendert.
MIT © BAUER GROUP. Brand-Assets (Logo, Markenwort „BAUER GROUP", Farbtoken) sind ausgenommen und bleiben Eigentum der BAUER GROUP.