Repository navigation
Commit 77db100
LAV-2704: Internal __snowflake$ SECURITY DEFINER helpers (exec_object_ddl, create_task, …) are callable from user SQL (#3119)
* LAV-2704: reject user references to internal helper schema
Add an ObjectName visitor boundary before procedure lowering and all canonical transform passes, returning Snowflake Cloud’s 002141 qualified-routine error before PostgreSQL can execute an internal helper. Document the trust boundary and capture function, procedure, and privilege-escalation regressions against Cloud.
Swept AST object-name positions through the generic visitor rather than individual expression/statement variants; relations, routines, types, casts, table functions, and nested visited statements all share the guard.
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
qualified function × rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown[SELECT]
qualified procedure × rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown[CALL]
SECURITY DEFINER mutation × rejection/absence -> tests/queries/test_internal_schema_guard.py::test_privileged_ddl_helper_cannot_create_objects
transform-introduced helpers × trusted path -> existing compat DDL/SHOW/DESC/RESULT_SCAN/table-procedure suites
executor-owned privileged DDL × trusted path -> existing function/procedure compat suites
* LAV-2704: enforce the user AST boundary across re-entry paths
Move the origin guard ahead of server rewrites, retain a trusted top-level transform entry for executor-generated AST, and reapply the guarded entry to stored and dynamic SQL. Preserve Cloud-specific routine, type, and relation error shapes.
Migrate every waivered wire dependency: public equivalents cover NULL/ANY behavior, while account, task, data-metric, and stage implementation probes use the PostgreSQL test-infrastructure route.
Swept the user-AST surface for the object-name escape pattern; explicitly covered function expressions, CALL/bare CALL, relations, casts/types, stored SQL bodies, literal dynamic SQL, and generic object-name positions; trusted server/result-scan and transform-generated paths remain outside the rejection pass.
test_edit_waiver: tests/queries/ddl/test_account_edition.py tests/queries/functions/test_array_functions.py tests/queries/functions/test_data_metric_functions.py tests/queries/functions/test_date_functions.py tests/queries/functions/test_decfloat_aggregates.py tests/queries/functions/test_overload_gate.py tests/queries/stages/test_stages.py tests/queries/test_any_casts.py tests/queries/test_any_comparison.py tests/queries/test_any_dispatch.py tests/queries/test_any_opclass.py tests/queries/test_null_comparison.py tests/queries/test_tasks.py
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
qualified function x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown
qualified procedure x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown
quoted/case-varied qualification x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown
internal type x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_unknown_in_every_user_object_position
internal relation x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_unknown_in_every_user_object_position
stored SQL body x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_rejected_in_stored_sql_body
dynamic SQL x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_unknown_in_every_user_object_position
SECURITY DEFINER mutation x rejection/absence -> tests/queries/test_internal_schema_guard.py::test_privileged_ddl_helper_cannot_create_objects
public function lifecycle x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
public procedure lifecycle x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
store-writing DDL x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
SHOW/DESC x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
RESULT_SCAN x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
table procedure x trusted path -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
implementation-only probes x infrastructure path -> tests/conftest.py::pg_execute
* LAV-2704: record compat test edit waivers
Record the spec-granted paths individually for the repository guard, whose waiver parser treats the spec's space-separated line as one path.
test_edit_waiver: tests/queries/ddl/test_account_edition.py
test_edit_waiver: tests/queries/functions/test_array_functions.py
test_edit_waiver: tests/queries/functions/test_data_metric_functions.py
test_edit_waiver: tests/queries/functions/test_date_functions.py
test_edit_waiver: tests/queries/functions/test_decfloat_aggregates.py
test_edit_waiver: tests/queries/functions/test_overload_gate.py
test_edit_waiver: tests/queries/stages/test_stages.py
test_edit_waiver: tests/queries/test_any_casts.py
test_edit_waiver: tests/queries/test_any_comparison.py
test_edit_waiver: tests/queries/test_any_dispatch.py
test_edit_waiver: tests/queries/test_any_opclass.py
test_edit_waiver: tests/queries/test_null_comparison.py
test_edit_waiver: tests/queries/test_tasks.py
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
qualified function/procedure and case variants x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_routines_are_unknown
internal object/type and dynamic SQL x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_unknown_in_every_user_object_position
stored SQL body x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_rejected_in_stored_sql_body
SECURITY DEFINER mutation x rejection/absence -> tests/queries/test_internal_schema_guard.py::test_privileged_ddl_helper_cannot_create_objects
public DDL and transformed paths x trusted execution -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
implementation-only probes x infrastructure path -> tests/conftest.py::pg_execute
* LAV-2704: guard opaque SQL function bodies
Apply the user-origin internal-schema guard immediately after reparsing opaque SQL UDF bodies, before transform_inner can introduce trusted identifiers. Cover scalar expression and statement bodies, scripting UDF blocks, and data metric function bodies.
Swept procedure_transformer and sibling data-metric lowering for the opaque-body reparse pattern; guarded every user-authored parsed AST before inner transformation; no other parse-to-transform_inner sibling remains.
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
scalar SQL UDF statement body x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_rejected_in_stored_sql_body
scalar SQL UDF bare-expression body x rejection -> guarded by the same transform_function_body branch; uncovered: parser fallback shares the identical guard and error path
scripting SQL UDF body x rejection -> guarded before scripting lowering; uncovered: same AST visitor and error path
SQL data metric body x rejection -> guarded before data-metric lowering; uncovered: same AST visitor and error path
trusted public function/procedure lifecycle x success -> tests/queries/test_internal_schema_guard.py::test_trusted_internal_paths_remain_usable
* LAV-2704: reject opaque internal-schema bodies
Reject case-varied internal-schema references when neither direct nor SELECT-wrapped SQL function body parsing succeeds, instead of forwarding the opaque body unchanged to PostgreSQL. Repurpose the stored-body regression to exercise this parse-failure fallback while matching the existing Cloud-captured 002141 error shape.
Swept transform_function_body for all parse exits; both successfully parsed branches use the AST guard, and the sole unparsed forwarding branch now applies the raw-text rejection required by the human decision.
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
parsed SQL UDF body x rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_unknown_in_every_user_object_position
opaque parse-failure SQL UDF body x case-varied rejection -> tests/queries/test_internal_schema_guard.py::test_internal_schema_is_rejected_in_stored_sql_body
opaque parse-failure body without internal schema x unchanged forwarding -> existing SQL UDF compat coverage
* LAV-2704: format merged guard imports
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
* LAV-2704: repair guarded compat infrastructure
Correct the PostgreSQL CSV alias used by account-edition inspection, keep NULL/ANY implementation probes outside the user transform boundary, and use public Snowflake expressions for the remaining parity cases.
Swept the failed changed-file compat set for the same wire-internal-type pattern; ARRAY_SLICE, date-part, and error-only ANY dispatch probes now use pg_execute, while all other ANY cases use public SQL.
test_edit_waiver: tests/queries/ddl/test_account_edition.py tests/queries/functions/test_array_functions.py tests/queries/functions/test_data_metric_functions.py tests/queries/functions/test_date_functions.py tests/queries/functions/test_decfloat_aggregates.py tests/queries/functions/test_overload_gate.py tests/queries/stages/test_stages.py tests/queries/test_any_casts.py tests/queries/test_any_comparison.py tests/queries/test_any_dispatch.py tests/queries/test_any_opclass.py tests/queries/test_null_comparison.py tests/queries/test_tasks.py
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
## Test matrix
account edition infrastructure read x success -> tests/queries/ddl/test_account_edition.py::test_create_account_persists_edition_and_is_reachable
ARRAY_SLICE snowflake_null overloads x NULL -> tests/queries/functions/test_array_functions.py::test_array_slice_snowflake_null_overloads
date-part snowflake_null overloads x NULL -> tests/queries/functions/test_date_functions.py::test_year_month_day_snowflake_null_overloads
ANY public input types x parity -> tests/queries/test_any_dispatch.py
ANY error-only dispatch x exact error -> tests/queries/test_any_dispatch.py::test_any_encrypt_raw_single_arg_errors and sibling error tests
---------
Co-authored-by: spiral <spiral@localhost>
Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>1 parent 6e94bec commit 77db100
2 files changed
Lines changed: 38 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
422 | 422 | | |
423 | 423 | | |
424 | 424 | | |
425 | | - | |
426 | 425 | | |
427 | 426 | | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
428 | 445 | | |
429 | 446 | | |
430 | 447 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
200 | 210 | | |
201 | 211 | | |
202 | 212 | | |
| |||
329 | 339 | | |
330 | 340 | | |
331 | 341 | | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
332 | 352 | | |
333 | 353 | | |
334 | 354 | | |
| |||
0 commit comments