Summary
df.start() captures the calling identity via GetUserId(), which returns current_user. In a SECURITY DEFINER context, this is the function owner, not the actual caller. This means all SQL nodes in the workflow execute as the definer's identity — the caller controls the fut argument content, so they can execute arbitrary SQL as the definer.
Example
-- Admin creates helpful wrapper
CREATE FUNCTION run_report(q TEXT) RETURNS TEXT
LANGUAGE SQL SECURITY DEFINER AS $$
SELECT df.start(df.sql(q), 'report');
$$;
GRANT EXECUTE ON FUNCTION run_report TO reporting_role;
-- Attacker (reporting_role) escalates:
SELECT run_report('DROP TABLE admin_secrets');
-- SQL executes as the admin who owns run_report
Assessment
This is by-design PostgreSQL SECURITY DEFINER semantics, and the test suite validates it (tests/e2e/sql/13_user_isolation.sql, Test 6b). However, it is easy for administrators to create dangerous wrappers without realizing the implication.
Recommended Actions
- Add a prominent warning in
USER_GUIDE.md about SECURITY DEFINER interaction with df.start().
- Consider adding a
df.start() option or GUC that forces session_user capture instead of current_user.
Context
- Security review Finding 5 (severity: Medium)
- Affected component:
src/dsl.rs, function start()
Summary
df.start()captures the calling identity viaGetUserId(), which returnscurrent_user. In aSECURITY DEFINERcontext, this is the function owner, not the actual caller. This means all SQL nodes in the workflow execute as the definer's identity — the caller controls thefutargument content, so they can execute arbitrary SQL as the definer.Example
Assessment
This is by-design PostgreSQL
SECURITY DEFINERsemantics, and the test suite validates it (tests/e2e/sql/13_user_isolation.sql, Test 6b). However, it is easy for administrators to create dangerous wrappers without realizing the implication.Recommended Actions
USER_GUIDE.mdaboutSECURITY DEFINERinteraction withdf.start().df.start()option or GUC that forcessession_usercapture instead ofcurrent_user.Context
src/dsl.rs, functionstart()