Skip to content

Conversation

@daryllimyt
Copy link
Contributor

@daryllimyt daryllimyt commented Jan 24, 2026

Summary

  • Add database-level multi-tenancy isolation using PostgreSQL native Row-Level Security (RLS)
  • Policies filter on organization_id and workspace_id columns
  • Feature flag controlled via TRACECAT__FEATURE_FLAGS=rls-enabled

Changes

  • Add RLS_ENABLED feature flag for controlled rollout
  • Add TracecatRLSViolationError exception for access violations
  • Create tracecat/db/rls.py with RLS context management functions
  • Modify get_async_session() to automatically set RLS context from ctx_role
  • Add get_async_session_bypass_rls() for system operations (migrations, admin tasks)
  • Create Alembic migration enabling RLS on all tenant-scoped tables
  • Add RLS audit logging module for security monitoring
  • Add unit and integration tests

RLS Policies

Scope Tables Filter Column
Workspace 26 tables (workflow, case, secret, etc.) workspace_id
Organization 2 tables (organization_secret, organization_setting) organization_id
Special workspace table Both org and workspace-level access

Test plan

  • Run unit tests: uv run pytest tests/unit/test_rls.py -v
  • Start cluster with RLS enabled: TRACECAT__FEATURE_FLAGS=rls-enabled just cluster up -d --seed
  • Run integration tests: uv run pytest tests/unit/test_rls_policies.py -v
  • Verify RLS policies in database: SELECT * FROM pg_policies WHERE tablename = 'workflow';
  • Test cross-tenant access is blocked via API

🤖 Generated with Claude Code


Summary by cubic

Adds PostgreSQL Row-Level Security to enforce tenant isolation at the database level. Sessions set org/workspace context so queries are filtered by policies; rollout is gated by a feature flag.

  • New Features

    • Enable RLS policies on tenant tables (workspace_id, organization_id); workspace supports org-level listing.
    • Auto-set RLS context from ctx_role in get_async_session; add get_async_session_bypass_rls for system tasks.
    • Add rls-enabled feature flag and frontend enum update.
    • Add TracecatRLSViolationError and audit logs for context and violations.
    • Add unit and integration tests.
  • Migration

    • Run Alembic migration to enable policies on tenant tables.
    • Enable with TRACECAT__FEATURE_FLAGS=rls-enabled.
    • Use get_async_session for normal code; reserve get_async_session_bypass_rls for migrations/admin jobs.
    • Optionally verify with SELECT * FROM pg_policies WHERE tablename = '';

      Written for commit b14dc73. Summary will update on new commits.

@daryllimyt daryllimyt added enhancement New feature or request engine Improvements or additions to the workflow engine labels Jan 24, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-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.

1 issue found across 10 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="tests/unit/test_rls.py">

<violation number="1" location="tests/unit/test_rls.py:6">
P2: Unit tests in `tests/unit` are expected to be integration-style without mocks. This file relies on AsyncMock/MagicMock/patch, which contradicts the repo’s testing guidelines and reduces confidence in RLS behavior. Consider using real DB/session fixtures or move these tests under a mocks-allowed suite.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa3a4852da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +84 to +88
# Use SET LOCAL to scope to current transaction
# This is safe with connection pooling since the settings are cleared on transaction end
await session.execute(
text(f"SET LOCAL {RLS_VAR_ORG_ID} = :org_id"),
{"org_id": org_id_str},

Choose a reason for hiding this comment

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

P1 Badge Reapply RLS context each transaction or avoid SET LOCAL

The RLS context is set with SET LOCAL, which only persists for the current transaction. get_async_session() calls this once when the session is created, so any later commit()/rollback() in the same session starts a new transaction without the RLS variables. At that point current_setting(..., true) is NULL and your policies will filter out all rows, breaking common flows like “insert → commit → refresh/query” in the same session. Consider using SET with connection checkin/checkout cleanup or reapplying SET LOCAL on every transaction begin.

Useful? React with 👍 / 👎.

Comment on lines +190 to 212
"""
async with AsyncSession(get_async_engine(), expire_on_commit=False) as session:
yield session

Choose a reason for hiding this comment

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

P2 Badge Bypass session doesn’t actually bypass RLS

get_async_session_bypass_rls() yields a session without setting any RLS variables. When RLS is enabled, your policies only bypass if current_setting(...) equals the all‑zero UUID; a missing setting evaluates to NULL and blocks all rows. Any admin or background job using this helper will see empty results instead of unrestricted access. This should set the bypass values (e.g., call set_rls_context(..., None) or a dedicated bypass helper).

Useful? React with 👍 / 👎.

@blacksmith-sh

This comment has been minimized.

@daryllimyt
Copy link
Contributor Author

Code review

Found 2 issues:

  1. Import statements in function bodies (CLAUDE.md says "You must NEVER put import statements in function bodies")

# Use actual SQLAlchemy model for testing
from tracecat.db.models import Workflow

Multiple test methods contain from tracecat.db.models import Workflow inside function bodies (lines 276, 292, 310, 325, 345, 378). These imports should be moved to the top of the file.

  1. get_async_session_bypass_rls() is defined but never used in the codebase

https://github.com/TracecatHQ/tracecat/blob/a6068a7e2334605e9d96b8b00cdca4c22f84523b/tracecat/db/engine.py#L179-L210

The bypass function and its context manager are defined but have zero imports or usages elsewhere. This appears to be dead code that should either be removed or its intended use should be implemented.

Generated with Claude Code (https://claude.ai/code)

  • If this code review was useful, please react with a thumbs up. Otherwise, react with a thumbs down.

Add database-level multi-tenancy isolation using PostgreSQL native RLS
on organization_id and workspace_id columns.

Key changes:
- Add RLS_ENABLED feature flag for controlled rollout
- Add TracecatRLSViolationError exception for access violations
- Create tracecat/db/rls.py with context management functions
- Modify get_async_session() to automatically set RLS context from ctx_role
- Add get_async_session_bypass_rls() for system operations
- Create Alembic migration enabling RLS on all tenant-scoped tables
- Add RLS audit logging module for security monitoring
- Add unit and integration tests for RLS functionality

RLS policies:
- Workspace-scoped tables (26): filtered by workspace_id
- Organization-scoped tables (2): filtered by organization_id
- Workspace table: supports both org and workspace-level access

Context is set via PostgreSQL session variables using SET LOCAL,
which is connection-pool safe (scoped to transaction).

Enable with: TRACECAT__FEATURE_FLAGS=rls-enabled
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine Improvements or additions to the workflow engine enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant