A permission change you have not run as the role it affects is a guess.
I built the first wave of hardening for a client this week, and the real lesson was not in the report. It was in what happened when I tried to apply the fixes I was most sure about.
Two of the three items I had labeled do this first would have taken the public site down. The first one looked like a one line win: revoke anonymous execute on three helper functions. Clean, obvious, safe. Except Postgres checks function execute permission inside the row level policy evaluation, and those policies call the helpers. So revoking execute did not just lock down the functions. It broke anonymous browsing entirely. The main listings query threw permission denied.
I only caught it because I did not trust my own confidence. I ran the change as the anonymous role inside a transaction and rolled it back. The second item was the same story. It breaks a public detail page that selects every column, so it needs a frontend refactor before the lock is safe.
So I shipped the one item that was genuinely safe and filed the other two properly, with the actual blast radius written down instead of a guess.
This is the part people skip. A fix that reads correct on the page can still be wrong in production, because the permission you are changing gets evaluated in a context you were not picturing. The report cannot see that. Your read of the code cannot see that. Only running it as the exact role it affects can.
A permission change you have not executed as the role it affects is a guess. Confidence is not a test. The transaction that you roll back is.
AI Diagnostic | All insights