Skip to content

F5 masking, Gate 1 step 3: unmask, the lookup, the access review, reads audited - #216

Open
SauravYadav12 wants to merge 3 commits into
mainfrom
feat/f5-mask-step3
Open

SauravYadav12 wants to merge 3 commits into
mainfrom
feat/f5-mask-step3

Conversation

@SauravYadav12

Copy link
Copy Markdown
Collaborator

Masking (F5), Gate 1 step 3: unmask, the lookup, the access review's column, and audited reads (design, section 10). Builds on #214 and #215.

What it does

  • fox mask unmask <email> --branch b [--off] [--yes] gives a masked account real values on one branch, the same view its owner has. Only a masked account can be given it.
    • Its role on that branch gets pgaudit.role = bb_audit_read. This is a superuser's setting, so the account can't undo it.
    • bb_audit_read holds SELECT on every masked table, so the audit log records this account's reads of marked tables, and only this account's. That finishes 3.1's last item.
    • If the audit log is off, the preview says so, because reads aren't logged until it's on.
    • The account's open sessions end. When a branch is deleted, its unmask grants go with it, and unmasking an account removes all of its grants.
  • The lookup, bb_mask.find(table, column, value): a masked account finds a person by an exact value without unmasking.
    • It only searches a column with its own rule, and returns the rows masked.
    • A value that isn't the column's type is refused without echoing it back.
    • Each account gets 30 calls a minute.
    • Each call is recorded (who, when, which column, how many rows found) where the account can't read it. The value is never recorded. fox mask lookups lists the record.
  • The access review's "Sees unmasked" column shows whether each account is an admin, not masked, or masked, with the branches it owns and where it holds unmask. The report's note on masking now changes once masking is set up.
  • API and console:
    • POST /api/masking/unmask and GET /api/masking/lookups, documented in openapi.yaml;
    • Unmask and Lookups tables on the Masking page, and the new column on the access review page;
    • security log kind mask.unmask.

Limits (in the docs)

  • The Gateway lets a non-admin reach only main and the branches it owns, where it sees real values anyway. So for now unmask matters on main, until a branch can be shared.
  • Resetting a branch makes it again from scratch, so it loses its unmask grants.

Checked live, then by the suites

On a throwaway PostgreSQL 18, the lookup:

  • finds rows by a hash, a partial and a fake mask;
  • refuses a column without a rule, a JSON path, a system table and an injected name;
  • keeps its record unreadable to the account and free of values;
  • stops at 30 calls a minute, and leaves no value in the server log.

ALTER ROLE … SET pgaudit.role works whether or not pgAudit is loaded, and the account can't override it.

The suite found that an empty grants file was written as null; it is now [].

Tests

All in the fox-test VM, on the final code:

  • make integration: 829/829. The new §11ak covers:
    • unmask refused for an account that isn't masked, and for a branch that doesn't exist;
    • the preview, which changes nothing;
    • real values with the audit role set, both reads logged with no values, and the account unable to undo it;
    • another branch's record listing nobody;
    • the access review before and after;
    • unmask taken off;
    • the lookup: by hash and by partial mask, only on a column with a rule, recorded without the value, 30 a minute, no value in the server log;
    • a deleted branch's unmask gone, and an unmasked account keeping none;
    • the security log.
  • integration-v2: 403/403.
  • integration-ui: 49/49, plus the upgrade test. The new test masks an account, gives it unmask, checks the access review lists it, then takes it off.
  • integration-f5: 57/57; integration-f1: 70/70; integration-f6: 71/71.
  • Web unit tests: 256/256.
  • Go unit tests in both editions, and go vet for macOS, Linux and Windows.
  • Feature docs and checklist updated (O3, H21's last item, O2); the PDFs were re-rendered.

Steps 4 (the access-path suite) and 5 (masked branches) follow.

…ew, reads audited

fox mask unmask <email> --branch b gives a masked account real values on one
branch, its role's pgaudit.role set to bb_audit_read so its reads of marked
tables are logged (closing 3.1). bb_mask.find(table, column, value) finds a
person by an exact value, masked, 30 a minute, each call recorded without the
value (fox mask lookups). The access review's Sees unmasked column. Two API
routes; Unmask and Lookups on the Masking page; the column on the access
review page.
@vercel

vercel Bot commented Nov 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
foxbyte-get Ready Ready Preview Nov 6, 2026 9:41am UTC

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.

1 participant