Corrective Action: My Value Path Portal — 6 Security Vulnerabilities Patched
Date: March 15, 2026 Category: Security / Authorization Failure Impact: 6 Tier 1 security vulnerabilities across the authenticated portal experience. Any authenticated user could impersonate others, access unauthorized data, and bypass admin controls. Public staging sitemap exposed full URL structure. Presentation analytics API was unauthenticated. Resolution: All 6 patched in commit
63bb1453. tsc passes clean.
Incident
What Happened
A 5-agent audit of the My Value Path portal (139 routes under /my-value-path/) uncovered 20 critical, 36 major, 30 minor, and 25 informational findings. Six findings were classified as Tier 1 security issues requiring immediate remediation.
These vulnerabilities existed since the portal's initial implementation. They were not caught because:
- The portal was built incrementally across many sessions without a security review
- Silent error swallowing (
.catch(() => [])) masked API failures that would have surfaced authorization gaps - localStorage-first patterns gave functional feedback that hid broken HubSpot integrations
The 6 Vulnerabilities
| # | Vulnerability | Severity | File |
|---|---|---|---|
| 1 | Impersonation cookie not bound to session — any user can set vf_impersonate via dev tools |
CRITICAL | src/lib/auth.ts |
| 2 | Onboarding endpoint has no ownership verification — any user can update any service record | CRITICAL | src/pages/api/onboarding/complete.ts |
| 3 | Planning admin uses email-substring check (email.includes('valuefirst')) |
CRITICAL | src/pages/my-value-path/planning/admin/index.astro |
| 4 | Open redirect in auth/verify — unvalidated redirect parameter | CRITICAL | src/pages/auth/verify.astro |
| 5 | Staging sitemap: prerender = true, zero auth — full URL structure publicly exposed |
CRITICAL | src/pages/my-value-path/command-center/staging/sitemap.astro |
| 6 | Presentation analytics API — zero authentication on GET and POST | MAJOR | src/pages/api/presentations/analytics/ |
Root Cause
1. No Security Review Process for Authenticated Routes
The portal grew organically. Each page was built to "work" (fetch data, render UI) without systematic authorization review. The middleware detects 7 roles but only sets variables — individual pages must check them. Most don't.
2. Impersonation Was a Dev Convenience That Shipped to Production
The vf_impersonate cookie was likely added for testing ("see what this person's portal looks like"). It was never gated behind admin role verification or session binding. Any user who discovers it can impersonate any other user by setting a cookie with a HubSpot contact ID.
3. Email-Substring Auth Is Not Auth
The admin page checked email.includes('valuefirst') || email.includes('admin'). This matches any email containing those substrings — including admin@malicious.com or myvaluefirst@gmail.com. This is pattern matching, not authorization.
4. Open Redirect Is a Known Attack Vector
The auth/verify page accepted a redirect query parameter and redirected to it without validation. An attacker could craft a magic link URL that redirects to a phishing site after successful authentication.
Fixes Applied (commit 63bb1453)
Fix 1: Impersonation Cookie Bound to Session + Role
Added getContactIdFromCookieAsync() in auth.ts:
- Validates the auth token FIRST (confirms the user is authenticated)
- Fetches the authenticated user's contact record from HubSpot
- Checks if the user has Head Coach role via
isHeadCoachRole() - Only then reads the impersonation cookie
- Non-Head-Coach users: impersonation cookie is silently ignored
Fix 2: Onboarding Ownership Verification
complete.ts now:
- Fetches all services associated to the authenticated contact
- Verifies the requested
serviceIdis in that association list - Returns 403 if the user doesn't own the service record
Fix 3: Role-Based Admin Check
planning/admin/index.astro now:
- Uses
canAccessCommandCenter(unifiedRole)instead of email substring - Properly detects unified role from the middleware context
- Added
export const prerender = false(was missing, needed for SSR auth)
Fix 4: Open Redirect Validation
auth/verify.astro now:
- Validates redirect starts with
/(relative path) - Rejects
//prefix (protocol-relative URLs that bypass origin) - Defaults to
/my-value-pathif validation fails
Fix 5: Staging Sitemap Auth
staging/sitemap.astro now:
- Changed
prerender = truetoprerender = false(SSR, not static) - Added full auth block: cookie check, contact fetch,
canAccessCommandCenter()gate - Unauthenticated/unauthorized users get 403
Fix 6: Presentation Analytics Auth
Both index.ts and [slug].ts now:
- Check
getContactIdFromCookie()on all handlers (GET and POST) - Return 401 JSON response if unauthenticated
Systemic Issues Identified (Not Yet Fixed)
The audit revealed three systemic patterns beyond these 6 fixes:
| Pattern | Scope | Status |
|---|---|---|
| localStorage masking HubSpot failures | 9 of 12 planning tools | Properties now created (Ledger reconciliation). Code wiring needed. |
Silent error swallowing (.catch(() => [])) |
25+ pages | Tracked in audit synthesis. Progressive remediation. |
| Authentication without authorization | ~60% of routes | Middleware has role detection. Pages need to use it. Route migration blueprint maps all 139 routes. |
Prevention
Immediate
- Security audit is now a proven capability — 5-agent parallel audit pattern documented. Can be repeated for any experience vertical.
- Route migration blueprint created at
/mnt/d/Leadership/audits/2026-03-15-my-value-path/route-migration-blueprint.md— maps every route with its auth status. - HubSpot property reconciliation completed — 10 missing properties created, 11 undocumented properties indexed, 3 enum mismatches flagged.
Structural
- Every new authenticated route MUST call
canAccessPath()or equivalent role check. The middleware function exists but was never called by individual pages. - No more email-substring checks anywhere in the codebase. Role-based authorization only.
- Impersonation requires session-bound, role-verified access. The
getContactIdFromCookieAsync()pattern established in this fix is the template.
Verification
tsc --noEmitpasses clean (these are SSR-only pages — no full build required per build-verification skill)- All 6 files compile without type errors
- Auth patterns verified against existing
canAccessCommandCenter()implementation - Property-index updated with 21 newly documented/created properties
Audit Reports
| Report | Path |
|---|---|
| Synthesis (111 findings) | /mnt/d/Leadership/audits/2026-03-15-my-value-path/synthesis.md |
| Auth & Infrastructure | /mnt/d/Leadership/audits/2026-03-15-my-value-path/auth-infrastructure.md |
| Command Center | /mnt/d/Leadership/audits/2026-03-15-my-value-path/command-center.md |
| Contributor & Media | /mnt/d/Leadership/audits/2026-03-15-my-value-path/contributor-media.md |
| Commerce & Objects | /mnt/d/Leadership/audits/2026-03-15-my-value-path/commerce-objects.md |
| Planning & Self-Service | /mnt/d/Leadership/audits/2026-03-15-my-value-path/planning-selfservice.md |
| Route Migration Blueprint | /mnt/d/Leadership/audits/2026-03-15-my-value-path/route-migration-blueprint.md |
| HubSpot Property Reconciliation | /mnt/d/Leadership/audits/2026-03-15-my-value-path/hubspot-property-reconciliation.md |
Corrective action documented by V. Commit 63bb1453. March 15, 2026.