Skip to content

test: stats() and sort_status() refuse a heap table for privilege before reading its kind (#1657 follow-up) - #1670

Merged
jdatcmd merged 1 commit into
mainfrom
test/1657-privilege-before-storage-id
Oct 26, 2026
Merged

jdatcmd merged 1 commit into
mainfrom
test/1657-privilege-before-storage-id

Conversation

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

@jdatcmd A follow-up from my #1657 review: nothing pinned the order of the two lines #1657 added.

The hole. get_storage_id checks SELECT with GetUserId(). Inside the SECURITY DEFINER stats() and sort_status() that's the definer, so the check passes for anyone, and the function then says relation "…" is not a columnar table for a heap table. Only the explicit require_caller_select refuses the caller, and only while it runs first.

run, PG17 stats_privilege, shell / twin sort_status_privilege, shell / twin
sidfirst on main (the two lines swapped in both functions) 13/13 / 3 tests pass 13/13 / 13/13
head, this branch 15/15 / 5 passed 15/15 / 15/15
sidfirst, this branch 1 red / SAME 1 1 red / SAME 1
head on each of 15, 16, 18, 19 15/15 / green 15/15 / green

The red in both suites is the new check, by the same name in shell and twin: "a role with no privilege on a heap table is refused for privilege, not told it is not columnar". Under sidfirst the unprivileged role gets not a columnar table instead of permission denied for table.

What's added, in both suites and both twins:

  • a heap table that no test role may read;
  • the refusal check: it wants permission denied for table <heap>;
  • a control: "a superuser asking about a heap table is told it is not columnar", which wants the function's own not-columnar message. Without it, the first check could pass on a call that refuses everyone.

Ledger.

  • sort_status_privilege already had its rows and gains 2: the refusal is dated by sidfirst, the control is never.
  • stats_privilege had no rows on main, so it enters whole with its 15 checks from five-major runs: 1 dated, 14 never. That moves suites_not_covered 105 → 104. My first push added only its 2 new rows, and the ceiling guard refused it.
  • Census: 3853 → 3868 never, 2243 → 2245 dated, 6113 rows; cluster_tests 2677 → 2681.

Gates: guards 474 passed, collection 2681, ledger gate rc=0, self-test PASSED.

🤖 Generated with Claude Code

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 3fa55de. Thank you for pinning my own lines: the order is load-bearing for exactly the reason you give, and nothing held it.

Measured on PG17 with sidfirst (the two lines swapped in both functions):

  • main: stats_privilege shell 13/13, twin 3 passed;
  • this head: stats_privilege shell 14 + 1 failed, "a role with no privilege on a heap table is refused for privilege, not told it is not columnar: got [0] want [1]"; twin 1 failed, 4 passed.

Clean:

  • PG15 stats_privilege: shell 15 passed + 0 failed + 0 unrunnable + 0 skipped = 15; twin 5 passed in 5.15s
  • PG15 sort_status_privilege: shell 15 passed + 0 failed + 0 unrunnable + 0 skipped = 15; twin 15 passed in 2.75s
  • PG19 stats_privilege: shell 15 passed + 0 failed + 0 unrunnable + 0 skipped = 15; twin 5 passed in 2.32s
  • PG19 sort_status_privilege: shell 15 passed + 0 failed + 0 unrunnable + 0 skipped = 15; twin 15 passed in 1.66s

It needs a restate onto current main. I'll re-approve one whose non-registry files are unchanged.

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved on prchk167, the one-pass restate onto main c7f0de4. restate_check.sh against my last approval: the PR's own change is the same in every content file; only the registries (budget, TESTS.md, expected_tests) differ, and the head's census agrees with its tsv.

…ore reading its kind (#1657 follow-up)

get_storage_id checks SELECT with GetUserId(), which inside the SECURITY DEFINER
stats() and sort_status() is the definer, so it passes for anyone and then says
"relation ... is not a columnar table" for a heap table. Only the explicit
require_caller_select refuses the caller, and only while it runs first. Nothing
pinned that order: with the two lines swapped in both functions ("sidfirst", from
the #1657 review), stats_privilege and sort_status_privilege stayed 13/13 in shell
and twin on PG17, because on their columnar table the caller check still refused
afterwards. An unprivileged caller probing a heap table would be told its kind
instead of being refused.

Each suite now has a heap table no test role may read, and two checks in both
harnesses:

- "a role with no privilege on a heap table is refused for privilege, not told it
  is not columnar": sidfirst reddens it, the same name in shell and twin, in both
  suites;
- "a superuser asking about a heap table is told it is not columnar": the control,
  so the first check cannot pass on a call that refuses everyone.

Head is 15/15 in shell and twin for both suites on each of 15-19.

Rows: sort_status_privilege already had its rows and gains these 2.
stats_privilege had none, so it enters the ledger whole with its 15 checks
(all green on 15-19, shell and twin alike): the refusal dated by sidfirst, 14
never. Census 4520 -> 4535 never, 3443 -> 3445 dated, 7980 rows;
suites_not_covered 28 -> 27; cluster_tests 4227 -> 4231.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OffgridwithJD
OffgridwithJD force-pushed the test/1657-privilege-before-storage-id branch from 3fa55de to 607fa3b Compare October 23, 2026 16:31
@OffgridwithJD

Copy link
Copy Markdown
Collaborator Author

@jdatcmd This is restated onto #1756's head 4c6acc10, so it goes in right after the census chain. It's still one commit: 3fa55dee → 607fa3b8. It needs your re-approval on the new head.

  • The diff outside the bookkeeping is identical, compared line by line per file: stats_privilege.sh, sort_status_privilege.sh, both twins and the check_ledger.tsv rows. Only the census numbers moved: the budget paragraph, expected_tests and the message.
  • New census: never 4520 → 4535, dated 3443 → 3445, 7980 rows; suites_not_covered 28 → 27; cluster_tests 4227 → 4231.
  • PG17 at 607fa3b8:
    • stats_privilege: shell 15/15, twin 5 tests carrying the 15 checks (as on the old head).
    • sort_status_privilege: shell 15/15, twin 15/15.
    • Both twins show the same reds as the shell (none).
  • Gate: guards 839 passed, collection 4231 = cluster_tests, census 4535 = the ledger, and the ledger gate passed.

Please merge it after #1748 and #1756.

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved on 607fa3b, the restate onto #1756. restate_check.sh against my approval on 3fa55de: every content file is the same change; only the budget and expected_tests differ, and the head's census agrees with its tsv (4535). CI 15/15.

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.

2 participants