Add SHOW COLUMNS access check to hasColumnInTable#108464
Conversation
Pre-PR validation gate (click to expand)
Session id: cron:clickhouse-worker-slot-17:20260625-113500 |
|
cc @pufit @vitlibar — could you review this? |
|
Workflow [PR], commit [0221328] Summary: ✅
AI ReviewSummaryThis PR closes the PR MetadataThe chosen The Exact replacement: Findings💡 Nits
Final Verdict
|
27fba87 to
7aa0d8d
Compare
|
Pushed a style fix (force-update to 7aa0d8d): the Style check flagged "Avoid holding a copy of ContextPtr in Functions". Dropped the separate `query_context` member and now store the query context in `WithContext` directly (read via `getContext()` for the `SHOW_COLUMNS` check), matching `InterpreterDescribeQuery`. Access semantics unchanged; re-verified both directions (bypass to ACCESS_DENIED, GRANT SHOW COLUMNS restores 1/0, existence oracle closed) and the test still matches the reference. |
7aa0d8d to
803717f
Compare
CI finish ledger — 803717fEvery failure below has an owner: a fixing PR (ours or external). The PR's own diff (
Session id: cron:our-pr-ci-monitor:20260625-213000 |
Ergus
left a comment
There was a problem hiding this comment.
At minimum, please confirm behavior of hasColumnInTable('', db, tbl, col) as an unprivileged user in a default single-node setup.
|
Confirmed and fixed (option 2, gate the remote path). Reproduced on a single node as an unprivileged user (only
Fix: the Test |
CI finish ledger — 72a9159CI fully finished on this head (Finish Workflow SUCCESS, all checks green). No failed checks.
Session id: cron:our-pr-ci-monitor:20260707-180000 |
|
@groeneai you didn't reply to all my review comments. WHy? |
72a9159 to
103dc1e
Compare
hasColumnInTable read table column metadata with no access check, so a user with no privileges on a table could still probe its column names and test table and database existence. DESCRIBE and SHOW CREATE TABLE on the same table are gated by SHOW COLUMNS, but this function was not. The function stored the global context, which has full access by construction, so a check against it would always pass. Keep the query context, which carries the caller's rights, and check SHOW COLUMNS on the raw database and table names before resolving the table, so a denial does not depend on the table or database existing (no existence oracle). The FunctionDocumentation Description notes the required privilege so the auto-generated SQL reference (built from system.functions) matches the new behavior. Rebased onto master after ClickHouse#110881 removed the remote-server overload, so only the local three-argument form remains and needs this gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103dc1e to
0221328
Compare
CI finish ledger - 0221328Every failure below has an owner (a fixing PR). Only
PR-caused: none. This PR's diff is a |
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered: 15/15 (100.00%) · Uncovered code |
9ec0c6f
|
Found via ClickGap automated review. Please close or comment if this is incorrect or needs adjustment. TL;DRAdd SHOW COLUMNS access check to hasColumnInTable ClickGap verified: reproduced it with the script below on Reproduction-- As an admin (default user):
CREATE USER probe IDENTIFIED WITH plaintext_password BY 'pw';
GRANT SELECT ON system.one TO probe;
CREATE TABLE default.secret (id UInt64, password String) ENGINE = MergeTree ORDER BY id;
-- Now connect as user `probe` (has ONLY SELECT on system.one, no grant on default.secret):
SELECT hasColumnInTable('default', 'secret', 'password');
-- Before the fix: returns 1 (column-existence/table-existence bypass with no privilege).
-- After the fix: fails with ACCESS_DENIED (SHOW COLUMNS required, like DESCRIBE).Bisect
Affects
Suggested next stepThe fix has landed on master; the affected release branches above still need the backport. Component: cc fix PR author: @groeneai ClickGap · Finding: Triage metadata |
|
Finding confirmed: the SHOW COLUMNS access check in |
Related: #110881
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
The
hasColumnInTablefunction now requires theSHOW COLUMNSprivilege on the target table, the same grant required byDESCRIBEandSHOW CREATE TABLE. Previously any user could callhasColumnInTableto probe column names and test table and database existence without any access check.Description
hasColumnInTableread table column metadata without an access check. A user with no privileges on a table (for example one granted onlySELECT ON system.one) could still enumerate its column names (the function returns 1/0), probe table existence (UNKNOWN_TABLEvs0), and probe database existence.DESCRIBEandSHOW CREATE TABLEon the same table are gated bySHOW COLUMNS; this function was a bypass.Rebased onto master after #110881 removed the remote-server overload, so only the local
hasColumnInTable(database, table, column)form remains and is the only path that needs this gate.Why the naive fix does not work
create()stored the global context (context_->getGlobalContext()), which has full access by construction (it never has a user id set), so acheckAccesson it would always pass. The fix keeps the query context, which carries the caller's rights.Fix
create()now retains the query context.executeImplchecksSHOW_COLUMNSon the raw database and table names before the table is resolved, so denial does not depend on the table or database existing (closing the existence oracle). This mirrorsInterpreterDescribeQuery(checkAccess(AccessType::SHOW_COLUMNS, table_id)).Testing
New stateless test
04401_has_column_in_table_access_check: a user with onlySELECT ON system.onegetsACCESS_DENIEDfromhasColumnInTablefor an existing table, a non-existing table, and a non-existing database (existence-independent), then succeeds afterGRANT SHOW COLUMNS. Verified locally that an unprivileged user got the column/existence result before this change (bypass) and getsACCESS_DENIEDafter (50/50 runs). The existing test00386_has_column_in_tableis unchanged and still passes (it runs as a full-access user).Version info
26.7.1.1206(included in26.7and later)