Serving the Midwest since 2014 · Licensed in 15 states

Security & Data Protection

Last updated: July 2026

Why has_role stays executable under RLS

Our backend uses Row-Level Security (RLS) to make sure users can only read or change data they are allowed to see. Several policies need to know whether the current user is an admin, and they do that by calling a small helper function named has_role.

PostgreSQL evaluates RLS USING and WITH CHECK expressions under the calling role. If authenticated users could not execute has_role, every admin-gated RLS policy would fail at planning time and the protected tables would become unreachable.

Keeping has_role executable is safe because the function:

  • Is marked STABLE and SECURITY DEFINER, so it runs with the privileges of its owner, not the caller.
  • Has a fixed SET search_path = public, preventing search-path injection.
  • Performs only a bounded SELECT EXISTS against public.user_roles.
  • Returns only a boolean and never writes data, reads unrelated tables, or exposes user details.

In short, has_role is the canonical RLS helper for this app; revoking it would break access control rather than improve it.

Resolved security findings

The following findings were reviewed and addressed. Each fix was verified by re-running the security scan before marking it complete.

  • Anon access to security definer functions

    ID: SUPA_anon_security_definer_function_executable

    Revoked EXECUTE on email-queue helper functions from the PUBLIC, anon, and authenticated roles so unauthenticated users cannot invoke them directly.

  • Authenticated access to security definer functions

    ID: SUPA_authenticated_security_definer_function_executable

    Revoked EXECUTE on email-queue helper functions from authenticated users. The has_role helper remains executable intentionally because every admin-gated RLS policy relies on it (see below).

  • Mutable search path in security definer functions

    ID: SUPA_function_search_path_mutable

    Added SET search_path = public, pgmq to the email-queue helper functions so object resolution is deterministic and cannot be redirected by a malicious search_path.

  • Missing INSERT policy on contact leads

    ID: contact_leads_no_insert_policy

    Added a RESTRICTIVE INSERT policy that blocks direct inserts by anon and authenticated roles. Contact and quote forms submit through a trusted server route that writes with the service role, so leads are still captured while direct client writes are denied.

How we handle form submissions

Contact and quote forms post to server-side routes, not directly to the database. Those routes validate the input, write the lead record using a trusted service role, and then trigger notification emails. Because direct client inserts are explicitly blocked by RLS, a browser or API client cannot add leads on its own.

Questions?

If you have questions about how we protect your information, email eortiz@healthmarkets.com or call 402-699-2586.

← Back to home