Skip to content

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

Description

@pinodeca

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

  1. Add a prominent warning in USER_GUIDE.md about SECURITY DEFINER interaction with df.start().
  2. 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()

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions