Access audit of baresql_fx_saas

26 October 2026, 12:00 UTC · PostgreSQL 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1) · 4 logins · BareSQL 0.1.0

1high
1medium
1low
4logins that read personal data
5columns with personal data
BareSQL logged in as postgres (a superuser) over an unencrypted connection and read the system catalogs inside one read-only transaction. It read up to 200 rows of each table to see what the columns hold; the values were looked at in memory and are not in this report. For every login, the server's own privilege checks were asked about every table and column, and each answer below comes with the grant, membership, ownership or view that gives it.

Findings

HIGHView support.ticket_context reads app.accounts, app.tickets with rights that skip row security, so these logins see every row: saas_agent.

view-bypasses-row-security · support.ticket_context

MEDIUMRow security on app.audit_log is not forced and its owner saas_migrator can log in: sessions logged in as saas_migrator see every row.

rls-owner-login · app.audit_log

LOWRow security is on for app.api_keys but it has no policies: only its owner and roles that bypass row security see any rows.

rls-no-policies · app.api_keys

Who can read personal data

LoginPersonal dataColumnsTablesChangesNotes
saas_admin_appemail addressperson's namephone numberkey, token or secret420bypassrls
saas_appemail addressperson's namephone number311
saas_agentemail addressperson's name210
saas_migratoremail address111

Columns and tables count base tables, partitions and materialized views, reached in any way listed below. Changes counts tables with personal data the login can insert into, update, delete from or empty.

Personal data in this database

ColumnHoldsHow BareSQL knowsRead by
app.accounts.email
table · text
email address
high confidence
30 of 30 sampled values are email addressessaas_admin_app, saas_agent, saas_app
app.accounts.full_name
table · text
person's name
medium confidence
the column name suggests person's namesaas_admin_app, saas_agent, saas_app
app.accounts.phone
table · text
phone number
high confidence
30 of 30 sampled values are phone numberssaas_admin_app, saas_app
app.api_keys.api_key
table · text
key, token or secret
high confidence
3 of 3 sampled values look like random tokens; 3 of 3 sampled values contain a key or tokensaas_admin_app
app.audit_log.actor_email
table · text
email address
high confidence
20 of 20 sampled values are email addressessaas_migrator
support.ticket_context.email
view · text
email address
high confidence
comes from app.accounts.emailsaas_agent
support.ticket_context.full_name
view · text
person's name
medium confidence
comes from app.accounts.full_namesaas_agent
support.ticket_context_invoker.email
view · text
email address
high confidence
comes from app.accounts.emailno audited login
support.ticket_context_invoker.full_name
view · text
person's name
medium confidence
comes from app.accounts.full_nameno audited login

Logins

saas_admin_app

bypassrls · member of no role · no password set

Personal data it can readHoldsHow
app.accounts.email
table
email address
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.accounts.full_name
table
person's name
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.accounts.phone
table
phone number
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.api_keys.api_key
table
key, token or secret
  • granted to saas_admin_app; all rows (BYPASSRLS)
12 other columns it can read
ColumnHow
app.accounts.id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.accounts.tenant_id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.api_keys.id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.api_keys.tenant_id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tenants.id
  • granted to saas_admin_app
app.tenants.name
  • granted to saas_admin_app
app.tickets.id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tickets.tenant_id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tickets.account_id
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tickets.subject
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tickets.body
  • granted to saas_admin_app; all rows (BYPASSRLS)
app.tickets.created_at
  • granted to saas_admin_app; all rows (BYPASSRLS)

saas_agent

member of saas_support · no password set

Personal data it can readHoldsHow
app.accounts.email
table
email address
  • through view support.ticket_context (rights of saas_owner); all rows (owner; row security not forced)
app.accounts.full_name
table
person's name
  • through view support.ticket_context (rights of saas_owner); all rows (owner; row security not forced)
support.ticket_context.email
view
email address
  • granted to saas_support
support.ticket_context.full_name
view
person's name
  • granted to saas_support
14 other columns it can read
ColumnHow
app.tickets.id
  • granted to saas_support; rows allowed by policy support_read
  • through view support.ticket_context (rights of saas_owner); all rows (owner; row security not forced)
  • through view support.ticket_list (rights of the reader); rows allowed by policy support_read
  • through view support.ticket_overview (rights of saas_owner), then through view support.ticket_list (rights of the reader); rows allowed by policy support_read
app.tickets.tenant_id
  • granted to saas_support; rows allowed by policy support_read
app.tickets.account_id
  • granted to saas_support; rows allowed by policy support_read
app.tickets.subject
  • granted to saas_support; rows allowed by policy support_read
  • through view support.ticket_context (rights of saas_owner); all rows (owner; row security not forced)
  • through view support.ticket_list (rights of the reader); rows allowed by policy support_read
  • through view support.ticket_overview (rights of saas_owner), then through view support.ticket_list (rights of the reader); rows allowed by policy support_read
app.tickets.body
  • granted to saas_support; rows allowed by policy support_read
app.tickets.created_at
  • granted to saas_support; rows allowed by policy support_read
support.invoice_summary.id
  • granted to saas_support
support.invoice_summary.amount
  • granted to saas_support
support.ticket_context.id
  • granted to saas_support
support.ticket_context.subject
  • granted to saas_support
support.ticket_list.id
  • granted to saas_support
support.ticket_list.subject
  • granted to saas_support
support.ticket_overview.id
  • granted to saas_support
support.ticket_overview.subject
  • granted to saas_support
6 columns it may select that give it nothing today
ColumnHow
app.invoices.id
  • through view support.invoice_summary (rights of saas_owner); no rows (row security on, no policy lets this role read)
app.invoices.amount
  • through view support.invoice_summary (rights of saas_owner); no rows (row security on, no policy lets this role read)
support.ticket_context_invoker.id
  • granted to saas_support; reading it fails: the view needs app.accounts.email, app.accounts.full_name and app.accounts.id, which the reader may not read
support.ticket_context_invoker.subject
  • granted to saas_support; reading it fails: the view needs app.accounts.email, app.accounts.full_name and app.accounts.id, which the reader may not read
support.ticket_context_invoker.email email address
  • granted to saas_support; reading it fails: the view needs app.accounts.email, app.accounts.full_name and app.accounts.id, which the reader may not read
support.ticket_context_invoker.full_name person's name
  • granted to saas_support; reading it fails: the view needs app.accounts.email, app.accounts.full_name and app.accounts.id, which the reader may not read

saas_app

member of no role · no password set

Personal data it can readHoldsHow
app.accounts.email
table
email address
  • granted to saas_app; rows allowed by policy tenant_isolation
app.accounts.full_name
table
person's name
  • granted to saas_app; rows allowed by policy tenant_isolation
app.accounts.phone
table
phone number
  • granted to saas_app; rows allowed by policy tenant_isolation
10 other columns it can read
ColumnHow
app.accounts.id
  • granted to saas_app; rows allowed by policy tenant_isolation
app.accounts.tenant_id
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tenants.id
  • granted to saas_app
app.tenants.name
  • granted to saas_app
app.tickets.id
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tickets.tenant_id
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tickets.account_id
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tickets.subject
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tickets.body
  • granted to saas_app; rows allowed by policy tenant_isolation
app.tickets.created_at
  • granted to saas_app; rows allowed by policy tenant_isolation
What it can change (6)
TablePrivilegeHow
app.accounts personal dataINSERT
  • granted to saas_app; rows allowed by policy tenant_isolation
app.accounts personal dataUPDATE
  • granted to saas_app; rows allowed by policy tenant_isolation
app.accounts personal dataDELETE
  • granted to saas_app; rows allowed by policy tenant_isolation
app.ticketsINSERT
  • granted to saas_app; rows allowed by policy tenant_isolation
app.ticketsUPDATE
  • granted to saas_app; rows allowed by policy tenant_isolation
app.ticketsDELETE
  • granted to saas_app; rows allowed by policy tenant_isolation

saas_migrator

member of no role · no password set

Personal data it can readHoldsHow
app.audit_log.actor_email
table
email address
  • owner saas_migrator; all rows (owner; row security not forced)
3 other columns it can read
ColumnHow
app.audit_log.id
  • owner saas_migrator; all rows (owner; row security not forced)
app.audit_log.tenant_id
  • owner saas_migrator; all rows (owner; row security not forced)
app.audit_log.action
  • owner saas_migrator; all rows (owner; row security not forced)
What it can change (4)
TablePrivilegeHow
app.audit_log personal dataINSERT
  • owner saas_migrator; all rows (owner; row security not forced)
app.audit_log personal dataUPDATE
  • owner saas_migrator; all rows (owner; row security not forced)
app.audit_log personal dataDELETE
  • owner saas_migrator; all rows (owner; row security not forced)
app.audit_log personal dataTRUNCATE
  • owner saas_migrator

Everyone (PUBLIC)

What every role can read, including roles created after this audit.

No personal data is open to every role.

Method

Schemas audited: app, public, support. Connection: postgres://[email protected]:5432/baresql_fx_saas, login method md5.

BareSQL never calls a function written in SQL or a procedural language, never reads views or foreign tables, and never writes. Sampled values are turned into text by their type's own output function, never by a cast, and each table is read on its own, without its partitions or child tables. Views are traced to the columns they read from their stored definitions, without running them.

Every fact in this report is also in audit.json, which baresql diff and baresql audit --baseline compare with a later audit.