From fe5504c0356b1e0c85319c947c6deb8ac1b213d0 Mon Sep 17 00:00:00 2001 From: myself-aas <129354542+myself-aas@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:45:50 +0000 Subject: [PATCH] Harden authz, XSS, and entitlements; restyle to Google Developers UI. Tighten Express identity, GitHub webhook ownership, Firestore report/blog rules, and public HTML escaping. Track a commented .env.example and drop MUI in favor of a DevSite-style layout. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> --- .env.example | 124 +-- .gitignore | 266 +------ CODE_REVIEW_FIXES.md | 96 +++ REAUDIT.md | 129 ++++ firestore.rules | 97 ++- index.html | 6 +- package.json | 4 - public/admin.html | 5 + public/ai-readiness.html | 25 +- public/blogs.html | 5 + public/compare.html | 5 + public/compliance.html | 25 +- public/contact.html | 5 + public/cookies.html | 5 + public/dashboard.html | 54 +- public/eco-audit.html | 25 +- public/health.html | 26 +- public/index.html | 14 +- public/latency.html | 25 +- public/llmo.html | 24 +- public/methodology.html | 5 + public/migration.html | 26 +- public/privacy.html | 5 + public/repo-scanner.html | 28 +- public/report.html | 9 +- public/reports.html | 15 +- public/security.html | 5 + public/terms.html | 5 + public/xss-guard.js | 35 + server/app.ts | 10 +- server/core/authz.ts | 63 ++ server/core/rateLimit.ts | 88 ++- server/routes/account.ts | 61 +- server/routes/clientLogs.ts | 9 +- server/routes/engines.ts | 63 +- server/routes/github.ts | 46 +- server/routes/notifications.ts | 28 +- server/routes/reports.ts | 12 +- server/routes/telemetry.ts | 18 +- src/App.tsx | 60 +- src/components/admin/UserManagementView.tsx | 1 - src/components/auth/AdminRoute.tsx | 31 +- src/components/auth/AuthDomainModal.tsx | 5 +- src/components/auth/ProtectedRoute.tsx | 6 +- src/components/common/EngineInput.tsx | 111 ++- src/components/common/GlobalFaqSection.tsx | 2 +- src/components/common/MarkdownRenderer.tsx | 28 +- .../common/RoleSimulatorFloatingBar.tsx | 4 + src/components/docs/DocsLayout.tsx | 724 ++++++++++-------- .../home/ArchitectureComparator.tsx | 120 --- src/components/home/EngineExplorer.tsx | 5 - src/components/home/EnzymeGrid.tsx | 99 ++- src/components/home/FeaturedAuditMetrics.tsx | 259 ++++--- src/components/home/FinalCTA.tsx | 112 +-- src/components/home/HeroAuditMock.tsx | 111 +++ src/components/home/HeroSection.tsx | 162 ++-- src/components/home/HowItWorks.tsx | 103 ++- src/components/home/MeshNetworkSection.tsx | 262 ------- src/components/home/SectionHeader.tsx | 50 ++ src/components/home/SevenDayTrialSection.tsx | 315 -------- src/components/home/SocialProof.tsx | 44 +- src/components/home/Testimonials.tsx | 113 +-- src/components/home/WorkflowSection.tsx | 272 ------- src/components/layout/DevSiteLayout.tsx | 338 ++++++++ src/components/layout/DynamicBanner.tsx | 134 ---- src/components/layout/Footer.tsx | 16 +- src/components/layout/GlobalBreadcrumb.tsx | 2 +- .../layout/LinearAmbientBackground.tsx | 54 +- src/components/layout/MobileBottomNav.tsx | 2 +- src/components/layout/Navbar.tsx | 255 +++--- src/components/layout/Sidebar.tsx | 110 +-- .../pricing/TelemetryRoiCalculator.tsx | 47 +- .../telemetry/AuditScoreMatrixRadar.tsx | 2 +- src/components/tool/EngineCharts.tsx | 54 +- src/components/tool/EngineReportDashboard.tsx | 64 +- src/components/ui/LinearCard.tsx | 66 ++ src/components/ui/index.ts | 1 + src/components/user/UserGithubWebhookView.tsx | 2 +- src/context/AuthContext.tsx | 38 +- src/context/RoleSecurityContext.tsx | 6 +- src/context/ThemeContext.tsx | 2 +- src/index.css | 172 ++++- src/lib/authClaims.ts | 5 + src/lib/emailService.ts | 6 +- src/lib/firebase.ts | 28 +- src/lib/sanitizeHref.ts | 33 + src/lib/serverAuth.ts | 24 +- src/lib/webhookService.ts | 23 +- src/pages/AdminDashboardPage.tsx | 13 +- src/pages/BlogEditorPage.tsx | 233 +++++- src/pages/ComparePage.tsx | 4 +- src/pages/ContactPage.tsx | 8 +- src/pages/LegalPage.tsx | 15 +- src/pages/LoginPage.tsx | 30 +- src/pages/MasterAuditExecutionPage.tsx | 345 ++++++++- src/pages/MasterAuditPage.tsx | 32 +- src/pages/MethodologyPage.tsx | 4 +- src/pages/PricingPage.tsx | 227 +++++- src/pages/ProductsPage.tsx | 4 +- src/pages/SignUpPage.tsx | 19 +- src/pages/ToolPage.tsx | 209 +++-- src/pages/UserDashboardPage.tsx | 10 +- src/pages/docs/ApiReferenceDoc.tsx | 2 +- src/pages/docs/EcoHoloDoc.tsx | 6 +- src/pages/docs/EdgeVmaxDoc.tsx | 8 +- src/pages/docs/GitLygaseDoc.tsx | 8 +- src/pages/docs/OrchestratorDoc.tsx | 2 +- src/pages/docs/RateLimitingDoc.tsx | 16 +- src/pages/docs/ScoringMatrixDoc.tsx | 18 +- src/pages/docs/SecurityDoc.tsx | 14 +- src/pages/docs/SynthShiftDoc.tsx | 2 +- src/pages/docs/SystemOverviewDoc.tsx | 74 +- src/pages/docs/VitalZymeDoc.tsx | 8 +- src/pages/playground/EnginePlaygroundPage.tsx | 4 +- src/store/useTelemetryHUDStore.ts | 9 +- src/tests/phase1Hardening.test.tsx | 10 +- src/tests/server/rateLimit.test.ts | 7 +- src/tests/setup.ts | 6 + .../ui/accessibilityAndContrast.test.tsx | 15 +- src/tests/unit/parseEngineOutput.test.ts | 23 + src/types/index.ts | 7 + src/utils/parseEngineOutput.ts | 79 ++ src/utils/rateLimiter.ts | 3 +- src/utils/rolePermissions.ts | 15 +- 124 files changed, 4232 insertions(+), 3236 deletions(-) create mode 100644 CODE_REVIEW_FIXES.md create mode 100644 REAUDIT.md create mode 100644 public/xss-guard.js create mode 100644 server/core/authz.ts delete mode 100644 src/components/home/ArchitectureComparator.tsx delete mode 100644 src/components/home/EngineExplorer.tsx create mode 100644 src/components/home/HeroAuditMock.tsx delete mode 100644 src/components/home/MeshNetworkSection.tsx create mode 100644 src/components/home/SectionHeader.tsx delete mode 100644 src/components/home/SevenDayTrialSection.tsx delete mode 100644 src/components/home/WorkflowSection.tsx create mode 100644 src/components/layout/DevSiteLayout.tsx delete mode 100644 src/components/layout/DynamicBanner.tsx create mode 100644 src/components/ui/LinearCard.tsx create mode 100644 src/lib/authClaims.ts create mode 100644 src/lib/sanitizeHref.ts create mode 100644 src/tests/unit/parseEngineOutput.test.ts create mode 100644 src/utils/parseEngineOutput.ts diff --git a/.env.example b/.env.example index fa6c60f..58ee57d 100644 --- a/.env.example +++ b/.env.example @@ -1,52 +1,86 @@ -CATALYST_WEBHOOK_URL= -DODOPAY_API_KEY= -DODOPAY_SANDBOX_MODE= -DODOPAY_WEBHOOK_SECRET= -MAILGUN_API_KEY= -MAILGUN_DOMAIN= -MAILGUN_FROM_EMAIL= -MAILGUN_FROM_NAME= -MAILGUN_HOST= -MONGODB_DB_NAME= -MONGODB_URI= -NEXTAUTH_SECRET= -NEXTAUTH_URL= -PAGESPEED_API_KEY= -UPSTASH_REDIS_REST_TOKEN= -UPSTASH_REDIS_REST_URL= -V2CHECKOUT_MERCHANT_CODE= -V2CHECKOUT_SANDBOX_MODE= -V2CHECKOUT_SECRET_KEY= -VITE_APP_URL= -VITE_ENABLE_ANALYTICS= -VITE_ENABLE_PERFORMANCE_MONITORING= -VITE_FIREBASE_API_KEY= -VITE_FIREBASE_APP_ID= -VITE_FIREBASE_AUTH_DOMAIN= -VITE_FIREBASE_MEASUREMENT_ID= -VITE_FIREBASE_MESSAGING_SENDER_ID= -VITE_FIREBASE_PROJECT_ID= -VITE_FIREBASE_STORAGE_BUCKET= - -# --- Phase 0 security hardening ------------------------------------------- -# GitHub webhook HMAC secret (sha256 signatures are verified over the raw body). -# Configure this in your GitHub repo webhook settings; leave empty to reject -# all GitHub webhook deliveries. +# CatalystLab environment template +# Copy to `.env` for local use: cp .env.example .env +# `.env` is gitignored. This file is tracked (see !.env.example in .gitignore). +# Leave secrets empty in this template. Production must set real values. + +# ============================================================================= +# Runtime +# ============================================================================= +# development | test | production (production is always default-deny for authz) +NODE_ENV=development +# HTTP listen address. Use 0.0.0.0 so the preview proxy can reach the server. +HOST=0.0.0.0 +PORT=3000 +# pino log level: fatal | error | warn | info | debug | trace +LOG_LEVEL=info + +# ============================================================================= +# Reverse proxy / client IP +# ============================================================================= +# Set to "true" ONLY when Express sits behind a trusted proxy (Cloud Run, nginx). +# When unset, X-Forwarded-For / CF-Connecting-IP are ignored for rate limits, +# client-log buckets, and telemetry geo hashing. +TRUST_PROXY= + +# ============================================================================= +# Auth / demo surfaces +# ============================================================================= +# Skip Firebase identity on GitHub/notifications/API-key demo routes. +# Honored ONLY when NODE_ENV=test (vitest). Ignored in development and production. +ALLOW_UNAUTH_DEMO= + +# Firebase Admin — server-side ID token verification (attachIdentity, state sync). +# Unset => requests with Bearer tokens fail closed (401); anonymous stay visitors. +# Inline JSON (raw or base64) takes precedence over a file path. +FIREBASE_SERVICE_ACCOUNT_JSON= +FIREBASE_SERVICE_ACCOUNT_PATH= +# Standard Google ADC path (used if FIREBASE_SERVICE_ACCOUNT_PATH is empty). +GOOGLE_APPLICATION_CREDENTIALS= + +# Comma-separated allowlist of cat_live_... keys accepted via the x-api-key header. +# Unset => API-key rate tier is disabled (fail closed). Never put keys in Authorization: Bearer. +VALID_API_KEYS= + +# ============================================================================= +# Webhooks (HMAC over the raw request body) +# ============================================================================= +# GitHub repo webhook secret. Empty => unsigned deliveries are rejected. GITHUB_WEBHOOK_SECRET= -# Webhook signing secrets for payment gateways (HMAC-SHA256 over raw body). -# Per-gateway value wins over the shared value; unset => deliveries rejected (503). +# Shared payment webhook secret. Per-gateway vars below override this. PAYMENTS_WEBHOOK_SECRET= PAYMENTS_WEBHOOK_SECRET_2CHECKOUT= PAYMENTS_WEBHOOK_SECRET_DODOPAY= -# Comma-separated allowlist of valid ``cat_live_...`` API keys. Unset => the -# API-key rate tier is disabled entirely (Phase 1 replaces this with hashed, -# persisted keys). -VALID_API_KEYS= +# ============================================================================= +# Payments (fail closed — missing credentials => 503, no fake checkout) +# ============================================================================= +V2CHECKOUT_MERCHANT_CODE= +V2CHECKOUT_SECRET_KEY= +# "false" = live 2Checkout URLs; any other value = sandbox +V2CHECKOUT_SANDBOX_MODE= -# Firebase Admin service account for server-side ID token verification -# (state sync auth). Either inline JSON (optionally base64) or a file path. -# Unset => /api/state/sync responds 401 to everyone (fail closed). -FIREBASE_SERVICE_ACCOUNT_JSON= -FIREBASE_SERVICE_ACCOUNT_PATH= +DODOPAY_API_KEY= +DODOPAY_WEBHOOK_SECRET= +# "false" = live Dodo URLs; any other value = sandbox +DODOPAY_SANDBOX_MODE= + +# ============================================================================= +# Rate limiting (optional distributed limiter) +# ============================================================================= +# When both are set, engine routes also consume an Upstash sliding window (120/min). +# Unset => in-memory limiter only (fine for single instance / tests). +UPSTASH_REDIS_REST_URL= +UPSTASH_REDIS_REST_TOKEN= + +# ============================================================================= +# Analytics / Mailgun (optional) +# ============================================================================= +MONGODB_URI= +MONGODB_DB_NAME= + +MAILGUN_API_KEY= +MAILGUN_DOMAIN= +MAILGUN_FROM_EMAIL= +MAILGUN_FROM_NAME= +MAILGUN_HOST= diff --git a/.gitignore b/.gitignore index c32df22..f670f74 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ dist/ .nuxt/ .output/ -# Miscellaneous +# Secrets — never commit real env files. The template (.env.example) is tracked. .env .env.* !.env.example @@ -48,273 +48,11 @@ Thumbs.db # Vercel .vercel -# Others -.cache -.tempdencies -node_modules/ - -# Testing -coverage/ - -# Production builds -build/ -dist/ -.next/ -.nuxt/ -.output/ - -# Miscellaneous -.env -.env.* -!.env.example - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# IDEs and editors -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Misc -.npm -.yarn -.pnp -.yarnrc -.yarnrc.yml -.yarn/install-state.gz - -# TypeScript -*.tsbuildinfo - -# Vercel -.vercel - -# Others -.cache -.tempdencies -node_modules/ - -# Testing -coverage/ - -# Production builds -build/ -dist/ -.next/ -.nuxt/ -.output/ - -# Miscellaneous -.env -.env.* -!.env.example - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# IDEs and editors -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Misc -.npm -.yarn -.pnp -.yarnrc -.yarnrc.yml -.yarn/install-state.gz - -# TypeScript -*.tsbuildinfo - -# Vercel -.vercel - -# Others -.cache -.tempdencies -node_modules/ - -# Testing -coverage/ - -# Production builds -build/ -dist/ -.next/ -.nuxt/ -.output/ - -# Miscellaneous -.env -.env.* -!.env.example - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# IDEs and editors -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Misc -.npm -.yarn -.pnp -.yarnrc -.yarnrc.yml -.yarn/install-state.gz - -# TypeScript -*.tsbuildinfo - -# Vercel -.vercel - -# Others -.cache -.tempdencies -node_modules/ - -# Testing -coverage/ - -# Production builds -build/ -dist/ -.next/ -.nuxt/ -.output/ - -# Miscellaneous -.env -.env.* -!.env.example - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# IDEs and editors -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Misc -.npm -.yarn -.pnp -.yarnrc -.yarnrc.yml -.yarn/install-state.gz - -# TypeScript -*.tsbuildinfo - -# Vercel -.vercel - -# Others -.cache -.tempdencies -node_modules/ - -# Testing -coverage/ - -# Production builds -build/ -dist/ -.next/ -.nuxt/ -.output/ - -# Miscellaneous -.env -.env.* -!.env.example - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# IDEs and editors -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# OS -.DS_Store -Thumbs.db - -# Misc -.npm -.yarn -.pnp -.yarnrc -.yarnrc.yml -.yarn/install-state.gz - -# TypeScript -*.tsbuildinfo - -# Vercel -.vercel - -# Others +# Cache .cache .temp - # Python tooling artifacts __pycache__/ *.pyc .coverage -coverage/ diff --git a/CODE_REVIEW_FIXES.md b/CODE_REVIEW_FIXES.md new file mode 100644 index 0000000..f61c5af --- /dev/null +++ b/CODE_REVIEW_FIXES.md @@ -0,0 +1,96 @@ +# Code-review fix reports (chronological) + +Each item is the original finding, what changed, and how to verify. + +## Critical + +### C1 — Firestore superadmin via email allowlist +**Issue:** `isSuperAdmin()` / `hasSuperAdminClaim()` treated four hardcoded emails as admin, so a spoofed or compromised Google account with those addresses bypassed claims. +**Fix:** Superadmin is exclusively `request.auth.token.role == 'superadmin'` or `token.superadmin == true`. Email lists removed from `firestore.rules`. +**Verify:** Rules no longer mention `shuvo*` emails; unauthenticated or claim-less users cannot write privileged collections. + +### C2 — Entitlements query `userId` vs `ownerId` / document id +**Issue:** `fetchEntitlements` queried `user_subscriptions` with `.where('userId', '==', uid)`, missing the real doc keyed by uid / `ownerId`. +**Fix:** `src/lib/serverAuth.ts` reads `user_subscriptions/{uid}`. Vitest mock updated to `.doc().get()` with `{ exists, data }`. +**Verify:** `phase1Hardening.test.tsx` “maps Firestore subscription docs…” passes with `plan: 'pro'`. + +### C3 — Discord `test-discord` ungated +**Issue:** `/api/notifications/webhook/test-discord` posted to caller-supplied URLs without auth. +**Fix:** Same `requireSuperadmin` gate as Slack/email (demo only when `ALLOW_UNAUTH_DEMO=true` and not production). +**Verify:** Production without identity → 401; signed-in non-admin → 403. + +### C4 — GitHub unauthenticated in-memory bus +**Issue:** List/create/stream/events were public; webhook secrets used `Math.random`; SSE broadcast to every subscriber. +**Fix:** `requireIdentity` on all GitHub routes except HMAC webhook. `ownerId` from verified uid. `crypto.randomBytes` for secrets. SSE `Map` filters events. +**Verify:** `api.test.ts` GitHub suite still 200 under test `ALLOW_UNAUTH_DEMO`; webhook HMAC tests unchanged. + +### C5 — Markdown / public HTML XSS +**Issue:** Markdown `[text](javascript:…)` and `public/*.html` interpolated engine output / URLs into `innerHTML`. +**Fix:** `sanitizeHref` in `MarkdownRenderer` (http/https/mailto/relative/# only). Shared `public/xss-guard.js` (`CLEscape` / `CLColorize`) included from static pages; interpolations escaped before colorize. +**Verify:** `javascript:` links render as text; terminal output with ` + + + + + + + + + + + + + + + + + @@ -237,9 +242,12 @@

📦 Repository Hygiene

+ + + + + + + + + + + + + + +