Skip to content

security: document SECURITY DEFINER interaction with df.start() and add start_use_session_user GUC#185

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/security-document-definer-interaction
Draft

security: document SECURITY DEFINER interaction with df.start() and add start_use_session_user GUC#185
Copilot wants to merge 3 commits into
mainfrom
copilot/security-document-definer-interaction

Conversation

Copy link
Copy Markdown

Copilot AI commented May 27, 2026

df.start() captures identity via GetUserId() (current_user). Inside a SECURITY DEFINER function, current_user is the function owner, so any SQL in the fut argument executes with the definer's privileges — even when supplied by an unprivileged caller.

Changes

src/lib.rs + src/types.rs

  • New GUC pg_durable.start_use_session_user (bool, default off, Suset/superuser-only, changeable at runtime)
  • start_use_session_user() helper reads the GUC

src/dsl.rs

  • df.start() now calls GetSessionUserId() when the GUC is on, GetUserId() otherwise — all downstream identity validation logic is unchanged

USER_GUIDE.md

  • Rewrote "How Identity Is Captured" to accurately reflect the single-OID capture model
  • New ⚠️ SECURITY DEFINER Warning subsection: dangerous pattern example, root cause, and three mitigations (avoid the pattern / enable the GUC / use SECURITY INVOKER)
  • Added item Refactor DSL to use nested JSON graph construction #5 to "Security Best Practices" cross-referencing the new warning

tests/e2e/sql/13_user_isolation.sql

  • Test 6c: with start_use_session_user = on, calling the same SECURITY DEFINER wrapper as iso_alice captures session_user = iso_alice (not the definer postgres), and the SQL node fails because alice has no access to the superuser-only table

Usage

-- Prevent SECURITY DEFINER wrappers from escalating identity (set in postgresql.conf
-- or ALTER DATABASE SET; superusers can also SET per-session):
SET pg_durable.start_use_session_user = on;

-- Now even inside a SECURITY DEFINER function, df.start() records session_user:
CREATE FUNCTION run_report(q TEXT) RETURNS TEXT
LANGUAGE SQL SECURITY DEFINER AS $$
    SELECT df.start(df.sql(q), 'report');  -- captures caller's login role, not owner
$$;

Note: Enabling this GUC changes identity capture for all df.start() calls in scope. If your application uses SET ROLE to submit workflows under a group role, the captured identity will revert to the session login role instead.

Copilot AI and others added 2 commits May 27, 2026 14:38
…sion_user GUC

- Add pg_durable.start_use_session_user GUC (Suset, superuser-only, default off)
  that captures session_user instead of current_user in df.start(), preventing
  SECURITY DEFINER wrappers from silently escalating the submitted identity.
- Add start_use_session_user() helper in src/types.rs.
- Update df.start() in src/dsl.rs to use GetSessionUserId() when GUC is on.
- Add prominent SECURITY DEFINER warning section to USER_GUIDE.md with
  an example, explanation, and three mitigation options.
- Add Security Best Practices item #5 for SECURITY DEFINER risk.
- Add E2E Test 6c in 13_user_isolation.sql to validate that with the GUC on,
  a SECURITY DEFINER wrapper captures session_user (not the definer).

Co-authored-by: pinodeca <32303022+pinodeca@users.noreply.github.com>
Co-authored-by: pinodeca <32303022+pinodeca@users.noreply.github.com>
Copilot AI changed the title [WIP] Document security definer interaction with df.start() security: document SECURITY DEFINER interaction with df.start() and add start_use_session_user GUC May 27, 2026
Copilot AI requested a review from pinodeca May 27, 2026 14:42
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.

Security: document SECURITY DEFINER interaction with df.start()

2 participants