Skip to content

security: harden auth, SSO, path handling, access control#50

Merged
GeiserX merged 11 commits intomainfrom
security/hardening-patch
Apr 2, 2026
Merged

security: harden auth, SSO, path handling, access control#50
GeiserX merged 11 commits intomainfrom
security/hardening-patch

Conversation

@GeiserX
Copy link
Copy Markdown
Owner

@GeiserX GeiserX commented Mar 31, 2026

Summary

Addresses 5 security findings from a breadth-first review of auth/session, CLI flows, teams/SSO, and content routes.

HIGH

  • CLI auth session hijacking — The signin page auto-submitted any cli_session query param as soon as the browser was authenticated, allowing an attacker to trick a victim into minting an API token for the attacker's CLI session. Fixed by adding an explicit "Authorize CLI" consent screen.
  • OIDC SSO callback seat bypass + broken redirect — SSO callback auto-created TeamMember without checking maxSeats. Also redirected to non-existent /auth/sso-complete. Fixed both: added seat enforcement and created the missing page.

MEDIUM

  • CLI pull path traversalrepository_path from the cloud was joined directly with the output directory, allowing ../ escape. Added safePath() guard in CLI and server-side sanitization.
  • Multi-team access resolution — TEAM visibility used findFirst({ where: { userId } }) returning an arbitrary team. Replaced with findUnique on the composite teamId_userId key. Billing endpoint now returns all memberships.

LOW

  • Download analytics exposure — GET stats for private/team blueprints were unauthenticated. Added ownership/membership check.

Files changed

  • src/app/auth/signin/page.tsx — CLI consent screen
  • src/app/api/auth/sso/callback/oidc/route.ts — maxSeats check
  • src/app/auth/sso-complete/page.tsx — new SSO completion page
  • cli/src/commands/pull.ts — safePath guard
  • src/app/api/v1/blueprints/route.ts — sanitize repository_path
  • src/lib/data/templates.ts — multi-team access fix
  • src/app/api/billing/status/route.ts — return all teams
  • src/app/api/blueprints/[id]/download/route.ts — auth check on stats

Test plan

  • CLI login flow: verify consent screen appears, cancelling doesn't create token
  • SSO login with team at max seats: verify rejection
  • lynxp pull with ../ in repository_path: verify blocked
  • Multi-team user accessing TEAM blueprint from second team
  • Unauthenticated GET to /api/blueprints/bp_xxx/download: verify 401

HIGH - CLI auth session hijacking (CVE-like):
  Require explicit user consent before completing CLI auth flow.
  Previously the signin page auto-submitted any cli_session param,
  allowing an attacker to mint a victim-scoped API token.

HIGH - OIDC SSO callback seat bypass + missing route:
  Add maxSeats enforcement before creating TeamMember via SSO.
  Create the missing /auth/sso-complete page that the OIDC callback
  redirects to.

MEDIUM - CLI pull path traversal:
  Add safePath() guard to reject ../ segments in repository_path.
  Server-side: strip traversal patterns from repository_path on save.

MEDIUM - Multi-team access resolution:
  Replace findFirst (returns arbitrary team) with findUnique on the
  composite teamId_userId key for TEAM visibility checks.
  Return all team memberships from billing/status endpoint.

LOW - Download analytics auth check:
  Require ownership or team membership to view download stats for
  non-public blueprints.
GeiserX added 2 commits March 31, 2026 12:47
… stats and paths

- Add SSO CredentialsProvider to auth.ts with HMAC signature validation
- Pass teamId through sso-complete page to complete the OIDC flow
- Move seat-limit check before user creation in SSO callback
- Strip bp_ prefix on user template DB queries in download stats route
- Apply path traversal sanitization to all repositoryPath write paths
- Fix multi-team support in team settings page and dashboard route
GeiserX added 8 commits March 31, 2026 13:17
…nload IDs

- Replace replayable HMAC-signed SSO URL with one-time nonce stored in
  VerificationToken (5-minute expiry, consumed on first use)
- Fix team settings page using wrong role for invitation loading
- Expose all teams + per-team blueprints from dashboard route
- Strip sys_ prefix for system template download counter updates
- Wrap SSO seat-limit check + user/membership creation in a serializable
  Prisma transaction to prevent concurrent logins exceeding maxSeats
- Create page now fetches all teams from billing/status and shows a team
  selector when user belongs to multiple teams
- Submit uses selectedTeamId instead of hardcoded first-team fallback
- Wrap invitation acceptance seat-limit check + membership creation in a
  serializable Prisma transaction (same pattern as SSO callback)
- Dashboard now renders team banners and blueprints for all teams, not
  just the first membership
- Fix tx parameter types and user nullability in SSO callback
- Both SSO callback and invitation acceptance now retry up to 3 times on
  Prisma P2034 (serialization conflict) instead of bubbling a 500
- "Share with Team" CTA passes teamId in URL query param
- Create page reads teamId from URL, preselects the target team and
  auto-sets visibility to TEAM when linked from a specific team panel
- Invitation acceptance now re-checks teamMember inside the serializable
  transaction, so parallel duplicate submissions are idempotent instead
  of hitting the unique constraint
- Empty-state "Share with Team" button now passes teamId query param
  (matching the header CTA fix from previous commit)
…g on auth redirect

- Both SSO and invitation retry loops now catch P2002 (unique constraint
  violation from concurrent inserts) alongside P2034 serialization errors
- Create page preserves full URL including ?teamId= when redirecting
  unauthenticated users to sign-in
The regex-based .replace(/\.\.[/\\]/g, "") was bypassable (e.g.
....// becomes ../ after one pass). Replace with posix.normalize()
plus prefix check that rejects any path traversal after normalization.

Fixes CodeQL high-severity alerts on PR #50.
@GeiserX GeiserX merged commit c0bfb8d into main Apr 2, 2026
14 checks passed
@GeiserX GeiserX deleted the security/hardening-patch branch April 2, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants