Skip to content

feat(tables): enforce RLS on dynamic workspace schemas#2212

Merged
daryllimyt merged 28 commits intomainfrom
feat/rls-dynamic-schema-model-a
Mar 30, 2026
Merged

feat(tables): enforce RLS on dynamic workspace schemas#2212
daryllimyt merged 28 commits intomainfrom
feat/rls-dynamic-schema-model-a

Conversation

@daryllimyt
Copy link
Copy Markdown
Contributor

@daryllimyt daryllimyt commented Feb 27, 2026

Summary

  • apply Model A protections to dynamic workspace schemas (tables_<ws_short>, custom_fields_<ws_short>) by introducing an internal tenant column (__tc_workspace_id)
  • enforce table-level RLS policies for dynamic physical tables at creation time
  • add an Alembic migration to backfill existing dynamic tables with __tc_workspace_id and enable RLS policy (rls_policy_dynamic_workspace)
  • hide internal __tc_* columns from API-facing row payloads and field listings

Changes

  • tracecat/tables/service.py
    • add dynamic-table RLS constants and policy application helper
    • add internal tenant column to newly created workspace physical tables
    • strip internal columns from row payloads returned by table and editor services
  • tracecat/custom_fields/service.py
    • apply RLS policy on workspace custom-fields base table initialization
    • block create/update/delete operations targeting internal column names
    • hide internal columns in custom field listings
  • tracecat/cases/service.py
    • add __tc_workspace_id to workspace case_fields physical table definition
    • mark __tc_workspace_id as reserved
  • alembic/versions/b5fc4168fe22_apply_model_a_rls_to_dynamic_workspace_.py
    • add/backfill __tc_workspace_id across dynamic workspace schemas
    • enable/drop RLS policy for dynamic tables in upgrade/downgrade

Validation

  • uv run ruff check tracecat/tables/service.py tracecat/custom_fields/service.py tracecat/cases/service.py alembic/versions/b5fc4168fe22_apply_model_a_rls_to_dynamic_workspace_.py
  • uv run basedpyright tracecat/tables/service.py tracecat/custom_fields/service.py tracecat/cases/service.py alembic/versions/b5fc4168fe22_apply_model_a_rls_to_dynamic_workspace_.py
  • uv run pytest tests/unit/test_tables_service.py tests/unit/test_case_fields_service.py (fails in this worktree due missing MinIO on localhost:9000)

QA

  • exercised cluster 3 at http://localhost:280 and signed in to the UI as seeded user test@tracecat.com
  • created case custom fields evidence_note (Text, default seeded note) and triage_score (Integer, default 7) in the UI
  • created CASE-0001, updated custom field values in the UI, and verified via docker exec ... psql that custom_fields_ws_1KyvEJCxa6QPAj3FTDFnfB.case_fields stored the row with the expected values and the correct __tc_workspace_id
  • created table inventory_checks with columns hostname and risk_score, inserted rows host-a / 10 and host-b / 25 in the UI, and verified via docker exec ... psql that tables_ws_1KyvEJCxa6QPAj3FTDFnfB.inventory_checks stored both rows with the correct __tc_workspace_id
  • verified the dynamic-table RLS policy on both physical tables is keyed on __tc_workspace_id

Summary by cubic

Enforces Model A RLS on dynamic workspace schemas using a hidden __tc_workspace_id and a reserved __tc_ namespace. Table/editor APIs now return only user-visible columns in a stable order; invalid case-field inputs return HTTP 400.

  • New Features

    • Apply RLS keyed to app.current_workspace_id with bypass via app.rls_bypass='on'; auto-enable rls_policy_dynamic_workspace for new tables_<ws> and custom_fields_<ws>.case_fields.
    • Add non-null, server-default __tc_workspace_id to all tables_<ws> tables and case_fields; reserve the __tc_ prefix (case-insensitive) across table/editor metadata and custom-field ops; treat null column-rename payloads as no-ops.
    • Project only visible columns from table/editor APIs (get_row/list_rows/lookup_rows/insert/update) and list_fields/get_columns (system fields first); hide internal columns; quote "schema"."table" identifiers.
  • Migration

    • Add/backfill __tc_workspace_id and enable the workspace RLS policy across existing tables_* and custom_fields_*; handle legacy table metadata aliases; migration chain linearized after merging main.
    • On exact-name collisions, rename a user __tc_workspace_id to migrated_tc_workspace_id (with numeric suffix if needed) and update metadata; preserve existing user-defined __tc_* columns and comments.
    • Downgrade restores the legacy column/comments, drops the policy, and removes the tenant column.

Written for commit 6a05cb0. Summary will update on new commits.

Copy link
Copy Markdown
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 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tracecat/tables/service.py">

<violation number="1" location="tracecat/tables/service.py:727">
P1: Security: `EditorService.insert_row` and `update_row` don't reject writes to internal `__tc_*` columns. Since `get_columns()` reflects all physical columns (including `__tc_workspace_id`), a caller could set/override the tenant column in their payload. While RLS `WITH CHECK` may block cross-workspace writes when active, this should be filtered at the application layer for defense-in-depth — matching the approach in `custom_fields/service.py` which blocks operations targeting internal column names. Consider filtering `params.data` (or `row_data`) to reject keys where `is_internal_column_name(key)` is true, similar to how output is already stripped.</violation>
</file>

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

Copy link
Copy Markdown

@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: 91943620a4

ℹ️ 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".

Copy link
Copy Markdown
Contributor Author

daryllimyt commented Feb 27, 2026

Copy link
Copy Markdown

@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: ed6b32a54a

ℹ️ 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".

Base automatically changed from feat/rls to main March 9, 2026 16:28
@daryllimyt daryllimyt force-pushed the feat/rls-dynamic-schema-model-a branch 2 times, most recently from 571c254 to 37f760c Compare March 9, 2026 16:39
Copy link
Copy Markdown

@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: d526d9861f

ℹ️ 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".

Copy link
Copy Markdown

@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: 7884ab64dd

ℹ️ 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".

Copy link
Copy Markdown

@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: 5a594d6062

ℹ️ 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".

@daryllimyt daryllimyt force-pushed the feat/rls-dynamic-schema-model-a branch from 5a594d6 to bc5f6b6 Compare March 9, 2026 21:03
Copy link
Copy Markdown

@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: 485fd501fe

ℹ️ 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".

Copy link
Copy Markdown
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.

2 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


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

<violation number="1" location="tests/unit/test_tables_service.py:158">
P2: This test encodes the wrong contract for the `__tc_*` internal namespace; allowing `__tc_shadow` means internal-prefixed columns will bypass both the write guard and the visibility filter.</violation>
</file>

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

<violation number="1" location="tests/unit/test_case_fields_service.py:183">
P2: Don't codify `__tc_*` as a user-available namespace; reserve it for internal columns.</violation>
</file>

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

Copy link
Copy Markdown

@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: 38342762f6

ℹ️ 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".

@daryllimyt daryllimyt force-pushed the feat/rls-dynamic-schema-model-a branch 2 times, most recently from 4937fa5 to ac4cbe2 Compare March 10, 2026 03:16
Copy link
Copy Markdown
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 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


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

<violation number="1" location="tests/unit/test_dynamic_workspace_service_rls.py:26">
P2: Shadow the autouse `workflow_bucket` fixture here so this unit suite does not fail on missing MinIO.</violation>
</file>

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

@daryllimyt daryllimyt requested a review from jordan-umusu March 10, 2026 20:04
@daryllimyt daryllimyt force-pushed the feat/rls-dynamic-schema-model-a branch from e45808b to 2eb724c Compare March 13, 2026 14:20
Copy link
Copy Markdown

@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: 2eb724cc9a

ℹ️ 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".

@daryllimyt daryllimyt had a problem deploying to internal-registry-ci March 29, 2026 19:07 — with GitHub Actions Failure
Copy link
Copy Markdown

@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: f0e4784e52

ℹ️ 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".

@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 19:17 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 19:18 — with GitHub Actions Inactive
Copy link
Copy Markdown

@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: 47c14a3a76

ℹ️ 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".

@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 20:30 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 20:30 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 20:31 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 29, 2026 20:31 — with GitHub Actions Inactive
@zeropath-ai
Copy link
Copy Markdown

zeropath-ai bot commented Mar 29, 2026

No security or compliance issues detected. Reviewed everything up to 6a05cb0.

Security Overview
Detected Code Changes

The diff is too large to display a summary of code changes.

Copy link
Copy Markdown
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 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tracecat/tables/service.py">

<violation number="1" location="tracecat/tables/service.py:618">
P2: Explicit `name: null` rename payloads are silently ignored instead of being rejected, so invalid update requests can succeed unexpectedly.</violation>
</file>

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

@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 30, 2026 02:12 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 30, 2026 02:12 — with GitHub Actions Inactive
@daryllimyt daryllimyt changed the title feat(tables): enforce model-a RLS on dynamic workspace schemas feat(tables): enforce RLS on dynamic workspace schemas Mar 30, 2026
Copy link
Copy Markdown

@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: 0aabc8bad4

ℹ️ 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".

@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 30, 2026 03:19 — with GitHub Actions Inactive
@daryllimyt daryllimyt temporarily deployed to internal-registry-ci March 30, 2026 03:20 — with GitHub Actions Inactive
@daryllimyt daryllimyt merged commit 7c186cc into main Mar 30, 2026
18 checks passed
@daryllimyt daryllimyt deleted the feat/rls-dynamic-schema-model-a branch March 30, 2026 12:44
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.

2 participants