Access audit of baresql_fx_shop
2 October 2026, 09:00 UTC · PostgreSQL 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1) · 7 logins · BareSQL 0.1.0
Findings
HIGHEvery role, including roles created later, can read personal data in shop.newsletter.
Details
- email (email)
HIGHshop.export_customers() runs with the rights of shop_owner and doesn't set search_path: whoever can run it may be able to make it run their own code.
MEDIUMshop_reporting is a member of pg_read_all_data: it reaches every table.
MEDIUMshop.count_orders() runs with the rights of shop_owner, who can read 11 personal data columns; it can be run by every role (PUBLIC).
MEDIUMshop.export_customers() runs with the rights of shop_owner, who can read 11 personal data columns; it can be run by shop_etl.
Who can read personal data
| Login | Personal data | Columns | Tables | Changes | Notes |
|---|---|---|---|---|---|
| shop_reporting | bank account (IBAN)payment card numberemail addressIP addressperson's namepassword or password hashphone numberkey, token or secret | 11 | 5 | 0 | |
| shop_app | payment card numberemail addressIP addressperson's namepassword or password hashphone number | 9 | 3 | 2 | |
| shop_alice | payment card numberemail addressperson's namephone number | 6 | 2 | 0 | |
| shop_bob | payment card numberemail addressperson's namephone number | 6 | 2 | 0 | some only after SET ROLE |
| shop_intern | payment card numberemail addressperson's namephone number | 6 | 2 | 0 | |
| shop_analyst | email address | 1 | 1 | 0 | |
| shop_etl | email address | 1 | 1 | 0 |
Personal data in this database
| Column | Holds | How BareSQL knows | Read by |
|---|---|---|---|
| shop.api_credentials.credential | key, token or secret | 4 of 4 sampled values look like random tokens; 4 of 4 sampled values contain a key or token | shop_reporting |
| shop.customers.first_name | person's name | the column name suggests person's name | shop_alice, shop_app, shop_bob, shop_intern, shop_reporting |
| shop.customers.last_name | person's name | the column name suggests person's name | shop_alice, shop_app, shop_bob, shop_intern, shop_reporting |
| shop.customers.email | email address | 40 of 40 sampled values are email addresses | shop_alice, shop_app, shop_bob, shop_intern, shop_reporting |
| shop.customers.phone | phone number | 40 of 40 sampled values are phone numbers | shop_alice, shop_app, shop_bob, shop_intern, shop_reporting |
| shop.customers.card_number | payment card number | 40 of 40 sampled values are payment card numbers (valid check digit) | shop_alice, shop_app, shop_bob, shop_intern, shop_reporting |
| shop.customers.notes | email address | 1 of 40 sampled values contain an email address | shop_app, shop_reporting |
| shop.newsletter.email | email address | 25 of 25 sampled values are email addresses | shop_alice, shop_analyst, shop_app, shop_bob, shop_etl, shop_intern, shop_reporting |
| shop.refunds.iban | bank account (IBAN) | 12 of 12 sampled values are IBANs (valid check digits) | shop_reporting |
| shop.users.password_hash | password or password hash | 20 of 20 sampled values are password hashes | shop_app, shop_reporting |
| shop.users.last_login_ip | IP address | 20 of 20 sampled values are IP addresses | shop_app, shop_reporting |
| support.customer_lookup.customer_name | person's name | comes from shop.customers.first_name; comes from shop.customers.last_name | shop_alice, shop_bob, shop_intern, shop_reporting |
| support.customer_lookup.email | email address | comes from shop.customers.email | shop_alice, shop_bob, shop_intern, shop_reporting |
| support.customer_lookup.phone | phone number | comes from shop.customers.phone | shop_alice, shop_bob, shop_intern, shop_reporting |
| support.customer_lookup.card_number | payment card number | comes from shop.customers.card_number | shop_alice, shop_bob, shop_intern, shop_reporting |
Logins
shop_alice
| Personal data it can read | Holds | How |
|---|---|---|
| shop.customers.first_name | person's name |
|
| shop.customers.last_name | person's name |
|
| shop.customers.email | email address |
|
| shop.customers.phone | phone number |
|
| shop.customers.card_number | payment card number |
|
| shop.newsletter.email | email address |
|
| support.customer_lookup.customer_name | person's name |
|
| support.customer_lookup.email | email address |
|
| support.customer_lookup.phone | phone number |
|
| support.customer_lookup.card_number | payment card number |
|
10 other columns it can read
| Column | How |
|---|---|
| shop.customers.id |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
| support.customer_lookup.id |
|
| support.order_status.id |
|
| support.order_status.shipped_at |
|
| support.order_status.tracking_number |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
shop_analyst
| Personal data it can read | Holds | How |
|---|---|---|
| shop.newsletter.email | email address |
|
10 other columns it can read
| Column | How |
|---|---|
| shop.customers.id |
|
| shop.customers.city |
|
| shop.customers.created_at |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.customer_id |
|
| shop.orders.total |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
shop_app
| Personal data it can read | Holds | How |
|---|---|---|
| shop.customers.first_name | person's name |
|
| shop.customers.last_name | person's name |
|
| shop.customers.email | email address |
|
| shop.customers.phone | phone number |
|
| shop.customers.card_number | payment card number |
|
| shop.customers.notes | email address |
|
| shop.newsletter.email | email address |
|
| shop.users.password_hash | password or password hash |
|
| shop.users.last_login_ip | IP address |
|
18 other columns it can read
| Column | How |
|---|---|
| shop.customers.id |
|
| shop.customers.city |
|
| shop.customers.created_at |
|
| shop.customers.marketing_opt_in |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.customer_id |
|
| shop.orders.total |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
| shop.products.id |
|
| shop.products.name |
|
| shop.products.sku |
|
| shop.products.price |
|
| shop.users.id |
|
| shop.users.username |
|
| shop.users.email_verified |
|
What it can change (5)
| Table | Privilege | How |
|---|---|---|
| shop.customers personal data | INSERT |
|
| shop.customers personal data | UPDATE |
|
| shop.orders | INSERT |
|
| shop.orders | UPDATE |
|
| shop.users personal data | UPDATE (password_hash, last_login_ip) |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
shop_bob
| Personal data it can read | Holds | How |
|---|---|---|
| shop.customers.first_name | person's name |
|
| shop.customers.last_name | person's name |
|
| shop.customers.email | email address |
|
| shop.customers.phone | phone number |
|
| shop.customers.card_number | payment card number |
|
| shop.newsletter.email | email address |
|
| support.customer_lookup.customer_name | person's name |
|
| support.customer_lookup.email | email address |
|
| support.customer_lookup.phone | phone number |
|
| support.customer_lookup.card_number | payment card number |
|
10 other columns it can read
| Column | How |
|---|---|
| shop.customers.id |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
| support.customer_lookup.id |
|
| support.order_status.id |
|
| support.order_status.shipped_at |
|
| support.order_status.tracking_number |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
shop_etl
| Personal data it can read | Holds | How |
|---|---|---|
| shop.newsletter.email | email address |
|
2 other columns it can read
| Column | How |
|---|---|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
Functions that run with someone else's rights (2)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
| shop.export_customers() | shop_owner no search_path |
|
shop_intern
| Personal data it can read | Holds | How |
|---|---|---|
| shop.customers.first_name | person's name |
|
| shop.customers.last_name | person's name |
|
| shop.customers.email | email address |
|
| shop.customers.phone | phone number |
|
| shop.customers.card_number | payment card number |
|
| shop.newsletter.email | email address |
|
| support.customer_lookup.customer_name | person's name |
|
| support.customer_lookup.email | email address |
|
| support.customer_lookup.phone | phone number |
|
| support.customer_lookup.card_number | payment card number |
|
10 other columns it can read
| Column | How |
|---|---|
| shop.customers.id |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
| support.customer_lookup.id |
|
| support.order_status.id |
|
| support.order_status.shipped_at |
|
| support.order_status.tracking_number |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
shop_reporting
| Personal data it can read | Holds | How |
|---|---|---|
| shop.api_credentials.credential | key, token or secret |
|
| shop.customers.first_name | person's name |
|
| shop.customers.last_name | person's name |
|
| shop.customers.email | email address |
|
| shop.customers.phone | phone number |
|
| shop.customers.card_number | payment card number |
|
| shop.customers.notes | email address |
|
| shop.newsletter.email | email address |
|
| shop.refunds.iban | bank account (IBAN) |
|
| shop.users.password_hash | password or password hash |
|
| shop.users.last_login_ip | IP address |
|
| support.customer_lookup.customer_name | person's name |
|
| support.customer_lookup.email | email address |
|
| support.customer_lookup.phone | phone number |
|
| support.customer_lookup.card_number | payment card number |
|
27 other columns it can read
| Column | How |
|---|---|
| shop.api_credentials.id |
|
| shop.api_credentials.service |
|
| shop.customers.id |
|
| shop.customers.city |
|
| shop.customers.created_at |
|
| shop.customers.marketing_opt_in |
|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
| shop.orders.id |
|
| shop.orders.customer_id |
|
| shop.orders.total |
|
| shop.orders.tracking_number |
|
| shop.orders.shipped_at |
|
| shop.products.id |
|
| shop.products.name |
|
| shop.products.sku |
|
| shop.products.price |
|
| shop.refunds.id |
|
| shop.refunds.order_id |
|
| shop.refunds.amount |
|
| shop.users.id |
|
| shop.users.username |
|
| shop.users.email_verified |
|
| support.customer_lookup.id |
|
| support.order_status.id |
|
| support.order_status.shipped_at |
|
| support.order_status.tracking_number |
|
Functions that run with someone else's rights (1)
| Function | Runs as | How |
|---|---|---|
| shop.count_orders() | shop_owner |
|
Everyone (PUBLIC)
What every role can read, including roles created after this audit.
| Personal data | Holds | How |
|---|---|---|
| shop.newsletter.email | email address |
|
2 other columns every role can read
| Column | How |
|---|---|
| shop.newsletter.id |
|
| shop.newsletter.subscribed_at |
|
Method
Schemas audited: public, shop, support. Connection: postgres://[email protected]:5432/baresql_fx_shop, 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.