Skip to content

Codebase Audit Report (Jan 2026)

  • Framework: Rails 8.1 API (with propshaft, turbo-rails, stimulus-rails).
  • Data Store: PostgreSQL (pg).
  • Background Jobs: solid_queue.
  • Caching: solid_cache.
  • Frontend: Tailwind CSS + Stimulus (Hotwire). JS Bundling via esbuild.
  1. Authentication & Accounts:
    • Models: User, Account, CompanySeat.
    • Auth Engines: devise (Sessions/Registrations) mixed with passwordless (Magic Links).
    • Controllers: Users::Registrations, Users::Sessions, Companies::Registrations.
  2. Voice Infrastructure (Twilio):
    • Frontend: Browser-based calling via @twilio/voice-sdk.
    • Token Vending: Api::CallsController#token (issues JWTs for the SDK).
    • Webhooks: Api::Webhooks::TwilioController (Handles incoming voice logic and status updates).
    • Testing: Api::TwilioCallsController (Manual call triggers).
  3. Financials:
    • Service: Ledger::CreateTransaction (Idempotent transaction creation).
    • Model: Transaction (updates Account balance via callbacks).

[!WARNING] Identity Confusion & Race Conditions

  1. The “User vs Seat” Race Condition:

    • Mechanism: The system auto-provisions CompanySeats for users logging in with a matching company domain (CompanySeat.fetch_resource_for_passwordless).
    • The Bug: fetch_resource_for_passwordless performs a find then create. This is a classic race condition. Two simultaneous login attempts for a new employee could create duplicate seats or error out, depending on database constraints.
    • Identity Shadowing: Users::SessionsController prioritizes finding a User record before checking for a CompanySeat.
      • If an employee signs up as an individual (User) before their CompanySeat is provisioned, they become a permanent User.
      • They will log in as a User (individual account) and never be routed to the CompanySeat (company account) flow, effectively blocking them from their company’s workspace unless manual intervention occurs.
  2. Company Registration Ambiguity:

    • Companies::RegistrationsController creates a User with a password, but the main SessionsController forces a magic link flow upon finding a user.
    • The Account validation normalize_allowed_email_domains blindly trusts the domain of the simplified email. If someone registers a company with ceo@gmail.com, does the system effectively whitelist gmail.com for that company?
    • Risk: If public domains (gmail, outlook) are not explicitly blocked in Account.rb logic (I saw no blocklist), a user could theoretically claim gmail.com as their company domain, potentially capturing other Gmail users into their account (or failing due to the multiple-account check in CompanySeat).

[!CAUTION] Missing Authorization & Financial Risk

  1. Caller Authorization Bypass (Api::Webhooks::TwilioController#voice):

    • The Flow: When a user creates a call in the browser, Twilio hits this webhook to ask “What do I do?”.
    • The Code: The controller validates the Twilio signature (Good!), BUT it proceeds to response.dial immediately without checking if the account has a positive balance.
    • The Risk: A user with $0.00 balance can initiate calls. The token endpoint checks balance, but tokens last 1 hour. A user can get a token, drain their balance, and continue calling for the rest of the hour because the webhook (the actual gatekeeper) doesn’t check funds.
  2. Testing Code in Production Path:

    • Api::TwilioCallsController#create contains hardcoded fallback numbers (+15005550006).
    • While guarded by ensure_non_production!, having this logic alongside real API controllers is risky. It should be strictly isolated or behind a feature flag class.
  1. Robust Transaction Creation:
    • Ledger::CreateTransaction is well-implemented. It uses idempotency_key, wraps logic in Account.transaction, and handles uniqueness errors.
    • Balance Updates: The Transaction model updates Account#cached_balance_cents in an after_commit hook using update_all (atomic increment). This is solid.

  1. Voice Authorization Fixed:

    • Updated Api::Webhooks::TwilioController#voice to check call.account.balance_cents > 0. Returns “Insufficient funds” if balance is empty.
  2. Strict Authentication Hardening:

    • Seat Provisioning: Removed Auto-Provisioning.
    • Identity Strategy: Implemented “Strict Domain Control”. If a domain is claimed by a company, users CANNOT log in as individuals. They are blocked and told to request access from their admin.
  3. Post-Launch Reimbursement:

    • Planned process to reimburse or transfer balances from “shadowed” individual accounts created before the strict block was put in place.