Skip to content

chore: Move permissions checks outside of Analyzer#26869

Merged
kevintang2022 merged 1 commit intoprestodb:masterfrom
kevintang2022:export-D89869624
Jan 21, 2026
Merged

chore: Move permissions checks outside of Analyzer#26869
kevintang2022 merged 1 commit intoprestodb:masterfrom
kevintang2022:export-D89869624

Conversation

@kevintang2022
Copy link
Copy Markdown
Contributor

@kevintang2022 kevintang2022 commented Dec 28, 2025

Description

Address the TODO comment in Analyzer class to remove the overloaded analyze method and decouple the analyzeSemantic and permissionChecking behaviors.

Motivation and Context

The overloaded analyze method makes it difficult to ensure that permissions checks are run exactly once for every query type. For example, for nested EXPLAIN statements, the permissions checks being run for each level of nesting because each level of nesting creates a new rewrite and Analysis. By removing these overloaded analyze method calls, it gives flexibility to remove redundant permissions checks.

The new interface method added to DataDefinitionTask#queryPermissionCheck is needed because sometimes, a DataDefinitionTask implementation (like CreateViewTask) will call analyzeSemantic followed by checkAccessPermissions on the Analysis inside of the execute method for the task. This ensures that the utilized resources will be permission checked. But some implementations of DataDefinitionTask do not ever call analyzeSemantic (like CreateTableTask), but we still need to be running the query permission check for these query shapes. So for non-Analyze tasks (CreateTable), we can just run the default implementation of query permissions check, and for Analyze tasks (CreateView), we override the default implementation to be NOOP and run the checkAccessPermissions after the analyze.

Impact

  • Remove overloaded analyze call in Analyzer
  • Add query permission check for DDL executions that call Analyze
  • Add query permission check for Presto Spark

Test Plan

Add unit test cases for checkQueryIntegrity and ensure that each shape runs the check at least once.

Contributor checklist

  • Please make sure your submission complies with our contributing guide, in particular code style and commit standards.
  • PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced.
  • Documented new properties (with its default value), SQL syntax, functions, or other functionality.
  • If release notes are required, they follow the release notes guidelines.
  • Adequate tests were added if applicable.
  • CI passed.
  • If adding new dependencies, verified they have an OpenSSF Scorecard score of 5.0 or higher (or obtained explicit TSC approval for lower scores).

Release Notes

Please follow release notes guidelines and fill in the release notes below.

== NO RELEASE NOTE ==

Differential Revision: D89869624

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Dec 28, 2025

Reviewer's Guide

Decouples semantic analysis from access-permission checks by removing the overloaded Analyzer.analyze() entry point, pushing explicit permission checking to callers (including DDL tasks, query explain/describe rewrites, and Presto Spark), and standardizing query-integrity checks for DDL/Session executions, with corresponding test updates for access control coverage.

Sequence diagram for DDLDefinitionExecution query permission checks

sequenceDiagram
    participant Client
    participant QueryStateMachine as QueryStateMachine
    participant DDLDefExec as DDLDefinitionExecution
    participant Task as DDLDefinitionTask
    participant AccessControl as AccessControl
    participant Analyzer as Analyzer
    participant AnalyzerUtil as AnalyzerUtil

    Client->>QueryStateMachine: submit DDL statement
    QueryStateMachine->>DDLDefExec: create execution(statement, query)

    DDLDefExec->>Task: executeTask()
    activate DDLDefExec

    DDLDefExec->>Task: queryPermissionCheck(accessControl, identity, context, query)
    alt default DataDefinitionTask implementation
        Task->>AccessControl: checkQueryIntegrity(identity, context, query, emptyMap, emptyMap)
        AccessControl-->>Task: ok
    else DDL task override (e.g. CreateViewTask)
        Task-->>DDLDefExec: no-op (permission already handled with analyze)
    end

    DDLDefExec->>Task: execute(statement, transactionManager, metadata, accessControl, session, parameters, warningCollector, query)
    activate Task

    Task->>Analyzer: new Analyzer(session, metadata, sqlParser, accessControl, optionalQueryExplainer, parameters, parameterLookup, warningCollector, query)
    Task->>Analyzer: analyzeSemantic(statement, isDescribe=false)
    Analyzer-->>Task: Analysis

    Task->>AnalyzerUtil: checkAccessPermissions(analysis.getAccessControlReferences(), query)
    AnalyzerUtil-->>Task: ok

    Task-->>DDLDefExec: ListenableFuture
    deactivate Task

    DDLDefExec-->>QueryStateMachine: ListenableFuture
    deactivate DDLDefExec

    QueryStateMachine-->>Client: DDL result
Loading

Sequence diagram for QueryExplainer and Describe rewrites permission checks

sequenceDiagram
    participant Client
    participant QueryExplainer as QueryExplainer
    participant DescribeInputRewrite as DescribeInputRewrite
    participant DescribeOutputRewrite as DescribeOutputRewrite
    participant Analyzer as Analyzer
    participant AnalyzerUtil as AnalyzerUtil

    Client->>QueryExplainer: analyze(session, statement, parameters, warningCollector, query)
    QueryExplainer->>Analyzer: new Analyzer(..., accessControl, optionalQueryExplainer, parameters, parameterLookup, warningCollector, query)
    QueryExplainer->>Analyzer: analyzeSemantic(statement, isDescribe=false)
    Analyzer-->>QueryExplainer: Analysis
    QueryExplainer->>AnalyzerUtil: checkAccessPermissionsForTable(analysis.getAccessControlReferences())
    QueryExplainer->>AnalyzerUtil: checkAccessPermissionsForColumns(analysis.getAccessControlReferences())
    AnalyzerUtil-->>QueryExplainer: ok
    QueryExplainer-->>Client: Analysis

    Client->>DescribeInputRewrite: rewrite(DescribeInput)
    DescribeInputRewrite->>Analyzer: new Analyzer(...)
    DescribeInputRewrite->>Analyzer: analyzeSemantic(innerStatement, isDescribe=true)
    Analyzer-->>DescribeInputRewrite: Analysis
    DescribeInputRewrite->>AnalyzerUtil: checkAccessPermissionsForTable(analysis.getAccessControlReferences())
    DescribeInputRewrite->>AnalyzerUtil: checkAccessPermissionsForColumns(analysis.getAccessControlReferences())
    AnalyzerUtil-->>DescribeInputRewrite: ok
    DescribeInputRewrite-->>Client: rewritten DescribeInput

    Client->>DescribeOutputRewrite: rewrite(DescribeOutput)
    DescribeOutputRewrite->>Analyzer: new Analyzer(...)
    DescribeOutputRewrite->>Analyzer: analyzeSemantic(innerStatement, isDescribe=true)
    Analyzer-->>DescribeOutputRewrite: Analysis
    DescribeOutputRewrite->>AnalyzerUtil: checkAccessPermissionsForTable(analysis.getAccessControlReferences())
    DescribeOutputRewrite->>AnalyzerUtil: checkAccessPermissionsForColumns(analysis.getAccessControlReferences())
    AnalyzerUtil-->>DescribeOutputRewrite: ok
    DescribeOutputRewrite-->>Client: rewritten DescribeOutput
Loading

Class diagram for Analyzer and DDL task permission responsibilities

classDiagram
    class Analyzer {
        - Session session
        - Metadata metadata
        - SqlParser sqlParser
        - AccessControl accessControl
        - Optional queryExplainer
        - List~Expression~ parameters
        - Map parameterLookup
        - WarningCollector warningCollector
        - String query
        + Analyzer(Session session, Metadata metadata, SqlParser sqlParser, AccessControl accessControl, Optional queryExplainer, List parameters, Map parameterLookup, WarningCollector warningCollector, String query)
        + Analysis analyzeSemantic(Statement statement, boolean isDescribe)
        + Analysis analyzeSemantic(Statement statement, Optional queryExplainer, boolean isDescribe)
    }

    class Analysis {
        + AccessControlReferences getAccessControlReferences()
        + Scope getRootScope()
        + RelationType getOutputDescriptor(Node node)
        + Map getFunctionHandles()
    }

    class AccessControlReferences {
    }

    class AnalyzerUtil {
        + void checkAccessPermissions(AccessControlReferences references, String query)
        + void checkAccessPermissionsForTable(AccessControlReferences references)
        + void checkAccessPermissionsForColumns(AccessControlReferences references)
    }

    class DataDefinitionTask~T~ {
        <<interface>>
        + String explain(T statement, List parameters)
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
    }

    class DDLDefinitionTask~T~ {
        <<interface>>
        + ListenableFuture execute(T statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List parameters, WarningCollector warningCollector, String query)
    }

    class DDLDefinitionExecution~T~ {
        - DDLDefinitionTask~T~ task
        - QueryStateMachine stateMachine
        - AccessControl accessControl
        - String query
        + ListenableFuture executeTask()
    }

    class SessionDefinitionTask~T~ {
        <<interface>>
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
    }

    class SessionDefinitionExecution~T~ {
        - SessionDefinitionTask~T~ task
        - QueryStateMachine stateMachine
        - AccessControl accessControl
        - String query
        + ListenableFuture executeTask()
    }

    class CreateViewTask {
        + ListenableFuture execute(CreateView statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List parameters, WarningCollector warningCollector, String query)
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
        - Analysis analyzeStatement(Statement statement, Session session, Metadata metadata, AccessControl accessControl, List parameters, WarningCollector warningCollector, String query)
    }

    class CreateFunctionTask {
        + ListenableFuture execute(CreateFunction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters, String query)
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
    }

    class DropFunctionTask {
        + ListenableFuture execute(DropFunction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters, String query)
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
    }

    class AlterFunctionTask {
        + ListenableFuture execute(AlterFunction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List parameters, WarningCollector warningCollector, String query)
        + void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query)
    }

    class CreateMaterializedViewTask {
        + ListenableFuture execute(CreateMaterializedView statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List parameters, WarningCollector warningCollector, String query)
    }

    class QueryExplainer {
        + Analysis analyze(Session session, Statement statement, List parameters, WarningCollector warningCollector, String query)
    }

    class DescribeInputRewrite {
        + Node rewrite(DescribeInput node, Void context)
    }

    class DescribeOutputRewrite {
        + Node rewrite(DescribeOutput node, Void context)
    }

    class PrestoSparkQueryPlanner {
        + PlanAndMore createQueryPlan(Session session, BuiltInPreparedQuery preparedQuery, WarningCollector warningCollector, String query)
    }

    class AccessControl {
        + void checkQueryIntegrity(Identity identity, AccessControlContext context, String query, Map tags, Map extraInfo)
        + void checkCanCreateView(Identity identity, AccessControlContext context, CatalogSchemaTableName view)
    }

    class DenyQueryIntegrityCheckSystemAccessControl {
        + void checkQueryIntegrity(Identity identity, AccessControlContext context, String query, Map tags, Map extraInfo)
        + void checkCanCreateView(Identity identity, AccessControlContext context, CatalogSchemaTableName view)
    }

    DataDefinitionTask <|.. DDLDefinitionTask
    DDLDefinitionTask <|-- CreateViewTask
    DDLDefinitionTask <|-- CreateFunctionTask
    DDLDefinitionTask <|-- DropFunctionTask
    DDLDefinitionTask <|-- AlterFunctionTask
    DDLDefinitionTask <|-- CreateMaterializedViewTask

    DDLDefinitionExecution --> DDLDefinitionTask : uses task
    SessionDefinitionExecution --> SessionDefinitionTask : uses task

    CreateViewTask ..> Analyzer : creates
    CreateViewTask ..> AnalyzerUtil : calls checkAccessPermissions

    CreateFunctionTask ..> Analyzer : creates
    CreateFunctionTask ..> AnalyzerUtil : calls checkAccessPermissions

    DropFunctionTask ..> Analyzer : creates
    DropFunctionTask ..> AnalyzerUtil : calls checkAccessPermissions

    AlterFunctionTask ..> Analyzer : creates
    AlterFunctionTask ..> AnalyzerUtil : calls checkAccessPermissions

    CreateMaterializedViewTask ..> Analyzer : creates
    CreateMaterializedViewTask ..> AnalyzerUtil : calls checkAccessPermissions

    QueryExplainer ..> Analyzer : creates
    QueryExplainer ..> AnalyzerUtil : calls table and column checks

    DescribeInputRewrite ..> Analyzer : creates
    DescribeInputRewrite ..> AnalyzerUtil : calls table and column checks

    DescribeOutputRewrite ..> Analyzer : creates
    DescribeOutputRewrite ..> AnalyzerUtil : calls table and column checks

    PrestoSparkQueryPlanner ..> Analyzer : creates
    PrestoSparkQueryPlanner ..> AnalyzerUtil : calls checkAccessPermissions

    AccessControl <|-- DenyQueryIntegrityCheckSystemAccessControl
Loading

Flow diagram for query permission checks across components

flowchart TD
    A[Client submits statement] --> B{Statement type}

    B --> C[Query SELECT or DML]
    B --> D[DDL statement]
    B --> E[Session statement]
    B --> F[Explain or Describe]
    B --> G[Presto Spark query]

    C --> H[Analyzer.analyzeSemantic]
    H --> I[Planner and access control checks<br/>checkAccessPermissionsForTable/Columns]

    D --> J[DDLDefinitionExecution]
    J --> K[task.queryPermissionCheck]
    K --> L[DataDefinitionTask default<br/>AccessControl.checkQueryIntegrity]
    J --> M[task.execute]
    M --> N[Task creates Analyzer]
    N --> O[Analyzer.analyzeSemantic]
    O --> P[checkAccessPermissions<br/>for DDL Analysis]

    E --> Q[SessionDefinitionExecution]
    Q --> R[task.queryPermissionCheck]
    R --> S[AccessControl.checkQueryIntegrity]
    Q --> T[task.execute]

    F --> U[QueryExplainer or Describe rewrites]
    U --> V[Analyzer.analyzeSemantic]
    V --> W[checkAccessPermissionsForTable/Columns]

    G --> X[PrestoSparkQueryPlanner]
    X --> Y[Analyzer.analyzeSemantic]
    Y --> Z[checkAccessPermissions<br/>for Spark Analysis]
Loading

File-Level Changes

Change Details Files
Remove overloaded Analyzer.analyze() and require callers to invoke semantic analysis and access-permissions checks explicitly.
  • Delete Analyzer.analyze(...) overloads that performed both semantic analysis and access checks.
  • Update Analyzer call sites in tests and production code to use analyzeSemantic(..., isDescribe) instead of analyze(...).
  • At each updated call site, obtain AccessControlReferences from Analysis and invoke table/column access checks explicitly where needed.
presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/AbstractAnalyzerTest.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestColumnAndSubfieldAnalyzer.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestUtilizedColumnsAnalyzer.java
presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/QueryExplainer.java
presto-main-base/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java
presto-main-base/src/main/java/com/facebook/presto/sql/rewrite/DescribeOutputRewrite.java
presto-spark-base/src/main/java/com/facebook/presto/spark/planner/PrestoSparkQueryPlanner.java
Ensure DDL and session-definition tasks perform query-integrity checks via a unified interface, while allowing specific tasks to override the behavior.
  • Extend DataDefinitionTask with a default queryPermissionCheck(...) method that calls AccessControl.checkQueryIntegrity(...).
  • Update DDLDefinitionExecution and SessionDefinitionExecution to delegate query-integrity checking to task.queryPermissionCheck(...) instead of calling AccessControl directly.
  • Add no-op queryPermissionCheck(...) overrides for CreateViewTask, CreateFunctionTask, DropFunctionTask, and AlterFunctionTask so they can optionally customize behavior without changing existing semantics.
presto-main-base/src/main/java/com/facebook/presto/execution/DataDefinitionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/DDLDefinitionExecution.java
presto-main-base/src/main/java/com/facebook/presto/execution/SessionDefinitionExecution.java
presto-main-base/src/main/java/com/facebook/presto/execution/CreateViewTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/DropFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/AlterFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/DDLDefinitionTask.java
Add or adjust permission checking for specific DDL operations (views, functions, materialized views) to ensure query integrity and access control are enforced exactly once.
  • In DDL tasks that analyze a query (e.g., CreateViewTask, CreateFunctionTask, DropFunctionTask, AlterFunctionTask, CreateMaterializedViewTask), switch to analyzeSemantic(...), then call checkAccessPermissions(...) with the resulting AccessControlReferences and query text.
  • Introduce Presto Spark query planner call to checkAccessPermissions(...) after semantic analysis, ensuring Spark queries participate in the same permission model.
  • Update DenyQueryIntegrityCheckSystemAccessControl to implement checkCanCreateView(...) so view-creation permissions are exercised under integrity checks.
presto-main-base/src/main/java/com/facebook/presto/execution/CreateViewTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/DropFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/AlterFunctionTask.java
presto-main-base/src/main/java/com/facebook/presto/execution/CreateMaterializedViewTask.java
presto-main-base/src/main/java/com/facebook/presto/security/DenyQueryIntegrityCheckSystemAccessControl.java
presto-spark-base/src/main/java/com/facebook/presto/spark/planner/PrestoSparkQueryPlanner.java
Strengthen tests to cover query-integrity checks for additional query types, including DDL operations related to views and functions.
  • Extend TestCheckAccessPermissionsForQueryTypes to assert that query-integrity failures are surfaced for create view, create function, alter function, and drop function statements.
  • Adjust analyzer-based tests to use analyzeSemantic(...) plus explicit table/column access-permission checks before asserting on access-control-related structures.
  • Minor formatting adjustment in AbstractAnalyzerTest for materialized view definition construction for readability.
presto-tests/src/test/java/com/facebook/presto/tests/TestCheckAccessPermissionsForQueryTypes.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/AbstractAnalyzerTest.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestColumnAndSubfieldAnalyzer.java
presto-main-base/src/test/java/com/facebook/presto/sql/analyzer/TestUtilizedColumnsAnalyzer.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@kevintang2022 kevintang2022 marked this pull request as draft December 28, 2025 21:49
kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Dec 28, 2025
Summary:

- Remove overloaded analyze call in Analyzer
- Add query permission check for DDL executions that call Analyze
- Add query permission check for Presto spark

Differential Revision: D89869624
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 4 issues, and left some high level feedback:

  • There is now a lot of repeated analyzeSemantic(...) + getAccessControlReferences() + checkAccessPermissions* boilerplate across tasks, explainers, and tests; consider adding a small helper/utility to centralize this pattern and reduce duplication.
  • The no-op queryPermissionCheck overrides in some DDL tasks override the default implementation that calls checkQueryIntegrity, which may be surprising to future maintainers; consider either reusing the default behavior or adding a brief comment/rationale where the check is intentionally disabled.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There is now a lot of repeated `analyzeSemantic(...)` + `getAccessControlReferences()` + `checkAccessPermissions*` boilerplate across tasks, explainers, and tests; consider adding a small helper/utility to centralize this pattern and reduce duplication.
- The no-op `queryPermissionCheck` overrides in some DDL tasks override the default implementation that calls `checkQueryIntegrity`, which may be surprising to future maintainers; consider either reusing the default behavior or adding a brief comment/rationale where the check is intentionally disabled.

## Individual Comments

### Comment 1
<location> `presto-main-base/src/main/java/com/facebook/presto/execution/CreateViewTask.java:124-125` </location>
<code_context>
         return immediateFuture(null);
     }
+
+    @Override
+    public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
 }
</code_context>

<issue_to_address>
**🚨 issue (security):** Overriding `queryPermissionCheck` with a no-op likely disables query integrity checks for this DDL path

Previously, `DDLDefinitionExecution` always invoked `accessControl.checkQueryIntegrity(...)` for DDL statements. By overriding `queryPermissionCheck` with an empty method, `CreateView` now skips these checks. If you intended to extend the check, please delegate to the default implementation or reimplement an equivalent check. If the goal is to bypass integrity checks for this path, this should be clearly documented and carefully validated for safety.
</issue_to_address>

### Comment 2
<location> `presto-main-base/src/main/java/com/facebook/presto/execution/CreateFunctionTask.java:112-113` </location>
<code_context>
         return immediateFuture(null);
     }
+
+    @Override
+    public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
 }
</code_context>

<issue_to_address>
**🚨 issue (security):** No-op `queryPermissionCheck` here also skips integrity checks for CREATE FUNCTION

This override replaces the default `DataDefinitionTask.queryPermissionCheck` (which runs `checkQueryIntegrity`) with a no-op, changing behavior from the previous implementation where `DDLDefinitionExecution` always enforced query integrity for this statement type. Unless this relaxation is explicitly required, please either call the default implementation or add an equivalent function-specific integrity check rather than disabling it entirely.
</issue_to_address>

### Comment 3
<location> `presto-main-base/src/main/java/com/facebook/presto/execution/DropFunctionTask.java:100-101` </location>
<code_context>
         return immediateFuture(null);
     }
+
+    @Override
+    public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
 }
</code_context>

<issue_to_address>
**🚨 issue (security):** DROP FUNCTION also bypasses `checkQueryIntegrity` via an empty `queryPermissionCheck`

This adds another special case that bypasses query integrity checks. Since other DDL still goes through `accessControl.checkQueryIntegrity(...)`, either reuse the default `queryPermissionCheck` implementation or document a strong rationale for exempting `DropFunction`. Otherwise this could unintentionally weaken integrity enforcement.
</issue_to_address>

### Comment 4
<location> `presto-main-base/src/main/java/com/facebook/presto/execution/AlterFunctionTask.java:95-96` </location>
<code_context>
         return immediateFuture(null);
     }
+
+    @Override
+    public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
 }
</code_context>

<issue_to_address>
**🚨 issue (security):** ALTER FUNCTION now opts out of query integrity checks as well

This override bypasses the global `checkQueryIntegrity` enforcement that applied before the refactor, which may weaken security around function modification. Please either rely on the default `queryPermissionCheck` implementation or add an equivalent integrity check instead of a no-op.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +124 to +125
@Override
public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 issue (security): Overriding queryPermissionCheck with a no-op likely disables query integrity checks for this DDL path

Previously, DDLDefinitionExecution always invoked accessControl.checkQueryIntegrity(...) for DDL statements. By overriding queryPermissionCheck with an empty method, CreateView now skips these checks. If you intended to extend the check, please delegate to the default implementation or reimplement an equivalent check. If the goal is to bypass integrity checks for this path, this should be clearly documented and carefully validated for safety.

Comment on lines +112 to +113
@Override
public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 issue (security): No-op queryPermissionCheck here also skips integrity checks for CREATE FUNCTION

This override replaces the default DataDefinitionTask.queryPermissionCheck (which runs checkQueryIntegrity) with a no-op, changing behavior from the previous implementation where DDLDefinitionExecution always enforced query integrity for this statement type. Unless this relaxation is explicitly required, please either call the default implementation or add an equivalent function-specific integrity check rather than disabling it entirely.

Comment on lines +100 to +101
@Override
public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 issue (security): DROP FUNCTION also bypasses checkQueryIntegrity via an empty queryPermissionCheck

This adds another special case that bypasses query integrity checks. Since other DDL still goes through accessControl.checkQueryIntegrity(...), either reuse the default queryPermissionCheck implementation or document a strong rationale for exempting DropFunction. Otherwise this could unintentionally weaken integrity enforcement.

Comment on lines +95 to +96
@Override
public void queryPermissionCheck(AccessControl accessControl, Identity identity, AccessControlContext context, String query) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 issue (security): ALTER FUNCTION now opts out of query integrity checks as well

This override bypasses the global checkQueryIntegrity enforcement that applied before the refactor, which may weaken security around function modification. Please either rely on the default queryPermissionCheck implementation or add an equivalent integrity check instead of a no-op.

kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Dec 29, 2025
Summary:

- Remove overloaded analyze call in Analyzer
- Add query permission check for DDL executions that call Analyze
- Add query permission check for Presto spark

Differential Revision: D89869624
@kevintang2022 kevintang2022 changed the title Move permissions checks outside of Analyzer chore: Move permissions checks outside of Analyzer Dec 29, 2025
kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Dec 29, 2025
@kevintang2022 kevintang2022 marked this pull request as ready for review December 29, 2025 23:36
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The pattern analyzeSemantic(...); AccessControlReferences refs = analysis.getAccessControlReferences(); checkAccessPermissions... is now duplicated across multiple call sites (QueryExplainer, Describe* rewrites, tests, Spark planner, various DDL tasks); consider reintroducing a small helper (e.g., a static utility or a thin wrapper around Analyzer) to centralize this sequence while still avoiding the old overloaded analyze API.
  • Several DDL tasks override queryPermissionCheck as a no-op and instead invoke checkAccessPermissions inside their analyze* methods; it would help future maintainers if this divergence from the DataDefinitionTask default were either documented or funneled through a common helper to make it clear that query integrity is intentionally handled via analysis rather than the executor hook.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The pattern `analyzeSemantic(...); AccessControlReferences refs = analysis.getAccessControlReferences(); checkAccessPermissions...` is now duplicated across multiple call sites (QueryExplainer, Describe* rewrites, tests, Spark planner, various DDL tasks); consider reintroducing a small helper (e.g., a static utility or a thin wrapper around `Analyzer`) to centralize this sequence while still avoiding the old overloaded `analyze` API.
- Several DDL tasks override `queryPermissionCheck` as a no-op and instead invoke `checkAccessPermissions` inside their `analyze*` methods; it would help future maintainers if this divergence from the `DataDefinitionTask` default were either documented or funneled through a common helper to make it clear that query integrity is intentionally handled via analysis rather than the executor hook.

## Individual Comments

### Comment 1
<location> `presto-main-base/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java:122-125` </location>
<code_context>
             // create  analysis for the query we are describing.
             Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters, parameterLookup, warningCollector, query);
-            Analysis analysis = analyzer.analyze(statement, true);
+            Analysis analysis = analyzer.analyzeSemantic(statement, true);
+            AccessControlReferences accessControlReferences = analysis.getAccessControlReferences();
+            checkAccessPermissionsForTable(accessControlReferences);
+            checkAccessPermissionsForColumns(accessControlReferences);

             // get all parameters in query
</code_context>

<issue_to_address>
**suggestion:** DescribeInputRewrite uses direct table/column checks instead of the new unified checkAccessPermissions helper.

Other call sites now rely on `AnalyzerUtil.checkAccessPermissions(analysis.getAccessControlReferences(), query)`, but this one still invokes the table/column checks directly. To avoid this path diverging if additional checks are added to the unified helper, please consider using the same helper here for consistent permission evaluation across query types, unless DESCRIBE is explicitly intended to behave differently.

Suggested implementation:

```java
import static com.facebook.presto.sql.analyzer.utils.ParameterExtractor.getParameters;
import static com.facebook.presto.util.AnalyzerUtil.checkAccessPermissions;
import static com.facebook.presto.util.AnalyzerUtil.createParsingOptions;
import static java.util.Objects.requireNonNull;

```

```java
            // create  analysis for the query we are describing.
            Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters, parameterLookup, warningCollector, query);
            Analysis analysis = analyzer.analyzeSemantic(statement, true);
            checkAccessPermissions(analysis.getAccessControlReferences(), query);

            // get all parameters in query
            List<Parameter> parameters = getParameters(statement);

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Dec 30, 2025
kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Dec 31, 2025
kevintang2022 added a commit to kevintang2022/presto that referenced this pull request Jan 5, 2026
Analysis analysis = analyzer.analyzeSemantic(statement, false);
AccessControlReferences accessControlReferences = analysis.getAccessControlReferences();
checkAccessPermissionsForTable(accessControlReferences);
checkAccessPermissionsForColumns(accessControlReferences);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Notice that in this file, analyze is called, but checkAccessPermissions is not called. Onl y the Table and Column checks are called. The query check is not called here.

This is the one example where query permission check should not be invoked, even though analyzer was run. This is due to the explain rewrite.

If query permission check was added here, then it would be invoked multiple times for an explain query, and once for each nesting of explain. However, the query permissions check should only be run once

return analyzer.analyze(statement);
Analysis analysis = analyzer.analyzeSemantic(statement, false);
AccessControlReferences accessControlReferences = analysis.getAccessControlReferences();
checkAccessPermissionsForTablesAndColumns(accessControlReferences);
Copy link
Copy Markdown
Contributor Author

@kevintang2022 kevintang2022 Jan 20, 2026

Choose a reason for hiding this comment

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

Notice that in this file, analyze is called, but checkAccessPermissions is not called. Onl y the Table and Column checks are called. The query check is not called here.

This is the one example where query permission check should not be invoked, even though analyzer was run. This is due to the explain rewrite.

If query permission check was added here, then it would be invoked multiple times for an explain query, and once for each nesting of explain. However, the query permissions check should only be run once.

This logic will be applied in any Query Rewriter that calls analyze, like DescribeInputRewrite/DescribeOutputRewrite/ExplainRewrite. These rewriters do another analysis in their subqueries, which can result in repeated query permissions checks

@kevintang2022 kevintang2022 merged commit f552971 into prestodb:master Jan 21, 2026
171 of 176 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants